der henry: C# update database

Hallo,

ich versuche gerade in eine Datenbank mehrerer Werte zu schreiben. Hierzu benutze den sql Befehl "update". Aktuell scheitert es an dem zufügen bzw. füllen der einzelnen Platzhalter.

Ich möchte mehrere unterschiedliche Felder in der Tabelle datapoints mit unterschiedlichen Werten füllen. Die Schlüssel mit jeweiligem Wert hole ich aus einem dictonary. ... beides Typ string.

Irgendwie habe ich ein Verständnisproblem mit AddWithValue .... oder muss ich die Platzhalter ganz anders füllen ?

	string sql = "UPDATE datapoints SET actvalue = @value WHERE plcvarname = @field";	
			
			using (var cmd = new MySqlCommand(sql, connection))
			{
				foreach (var dic in plcactvalue)
				{ 
					cmd.Parameters.AddWithValue("@field",dic.Key);
					cmd.Parameters.AddWithValue("@value",dic.Value );	
					Console.WriteLine($"Schlüssel: {dic.Key}, Wert: {dic.Value}");
				}	
				cmd.ExecuteNonQuery();
			}

Vielen Dank !!

  1. "execute" muss für jeden Eintrag ausgeführt werden.

  2. Hallo Henry,

    man muss das SqlCommand-Objekt nicht für jeden Wert neu erzeugen. Es schadet vermutlich nichts, weil der Server identisches SQL erkennen und den generierten Plan wiederverwenden sollte, aber eigentlich programmiert man das so:

    string sql = "UPDATE datapoints SET actvalue = @value WHERE plcvarname = @field";	
    using (var cmd = new MySqlCommand(sql,connection) {
      cmd.Prepare();
    
      // Setze die Typen passend zu deiner DB
      cmd.Parameters.Add("@field", MySqlDbType.???);
      cmd.Parameters.Add("@value", MySqlDbType.???);
    
    
      foreach (var dic in plcactvalue)
      { 
        cmd.Parameters["@field"] = dic.Key;
        cmd.Parameters["@value"] = dic.Value;
    
        Console.WriteLine($"Schlüssel: {dic.Key}, Wert: {dic.Value}");
        cmd.ExecuteNonQuery();
      }
    }
    

    Hier sind die definierten Werte für MySqlDbType. Das ist ein enum, d.h. Du musst bspw. MySqlDbType.Int32 angeben wenn die DB-Column ein 32-bit Integer ist.

    Ob cmd.Prepare() in .net nötig ist, darüber scheiden sich auch die Geister. In PHP ist der Prepare() nötig, weil Du sonst kein mysqli_stmt-Objekt hast. In .net sollte es der erste ExecuteNonQuery() implizit tun. Die Doku von .net Framework UND von MS SQL Server ist da sehr vage, sie sagt nur, dass es die Methode gibt, aber nicht, was sie genau tut und ob sie Nutzen hat.

    Die Beschreibung von Prepare bei MySQL ist hingegen sehr klar: Unbedingt machen. Und zwar vor der Parameter-Deklaration.

    Probiere aus, ob Prepare einen Performance-Effekt für Dich hat.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf, Hallo encoder,

      vielen Dank für eure Unterstützung und noch wichtiger die Erklärung dazu.

      Mit cmd.prepare stützt mein Programm ab.

      Ein cmd. sollte in meinem Editor alle möglichen Parameter zu cmd anzeigen, prepare gibt es hier nicht.

      ... aber es funktioniert auch ohne cmd.prepare

      Was mir nicht gefällt, da ich eine Schleife für alle zu "Schreibenden" Werte zur Datenbank haben muss. d.h. bei jedem Wert mache ich einen Schreibzugriff ...

      Ich hatte bei einem node-Projekt von mir, einen ähnlichen Fall. Hier konnte jeweils ein Array für "key und value" übergeben werden und es wurde alles mit einem "Schreibzugriff" in die Datenbank geschrieben.

      Gibt es unter c#/.net nicht ähnliches ??

      Gruß
      
      1. Hallo der henry,

        Gibt es unter c#/.net nicht ähnliches ??

        Das ist kein C#-Problem. Es gibt in SQL keinen "Array-Update".

        WOLLTE ICH SCHREIBEN. Aber dann hab ich gegoogelt, und MySQL bietet 2 Möglichkeiten.

        Aber bevor du damit anfängst:

        • Läuft der DB-Server auf dem gleichen Gerät wie der Webserver? Wenn ja, sind Server-Roundtrips billig. Wenn nein, sieht die Sache schon anders aus.
        • Wieviele plcvarnames aktualisierst Du? Hast Du mit den Einzel-Updates Performance-Probleme? Wenn nicht, dann fang nicht das Optimieren an.

        Zu den möglichen Lösungen:

        (1) der ON DUPLICATE KEY Trick

        Voraussetzung: plcvarname ist ein unique key in der datapoints-Tabelle

        INSERT INTO datapoints (plcvarname, actvalue)
        values (@key1, @value1),
               (@key2, @value2),
               (@key3, @value3)
        ON DUPLICATE KEY
           UPDATE actvalue = VALUES(actvalue)
        

        Information zu VALUES()

        Den Values-Teil kannst Du mit C# in einem StringBuilder generieren (bitte nicht mit String-Verkettung in einer Schleife), und gleichzeitig fügst Du pro Durchlauf dem SqlCommand zwei Parameter mit den Werten hinzu.

        Oder Du verzichtest auf Parameter und verwendest die EscapeString-Methode vom MySqlHelper, um die plcvarname Werte für den Kontextwechsel vorzubereiten. Bei den actvalue-Werten hängt die richtige Behandlung vom Datentyp ab. Sind das Zahlen oder auch Strings?

        Du musst natürlich aufpassen, dass Du nicht versehentlich einen unbekannten plcvarname verwendest, sonst hast Du eine neue Row erzeugt.

        (2) Das REPLACE Statement. Schau es Dir in der MySQL/MariaDB Doku an. Ob es für Dich nutzbar ist, hängt von deinen Daten ab.

        Das Thema Parameter vs EscapeString hast Du hier auch.


        Unabhängig von MySQL gibt es auch eine Möglichkeit 3, die setzt aber voraus, dass die plcvarname Werte klar definiert sind und Du immer ein bestimmtes Set von plcvarnames setzt. Wenn Du bspw. immer die plcvarnames foo, bar und baz gemeinsam setzt, dann schreibe eine Prozedur, die diese drei Parameter bekommt und dann auf dem Server, ohne weitere Server-Roundtrips, die Updates macht. Diese Prozedur rufst Du mit einem MySqlCommand vom CommandType StoredProcedure auf.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. OK, Danke

          ... da lass ich erst einmal die Finger weg.

      2. Moin,

        Was mir nicht gefällt, da ich eine Schleife für alle zu "Schreibenden" Werte zur Datenbank haben muss. d.h. bei jedem Wert mache ich einen Schreibzugriff ...

        Du machst für jeden Datensatz aus dem Array ein INSERT/UPDATE. Wenn Du die Möglichkeit hast so genannte „Batch-Inserts“ zu nutzen, dann solltest Du das unbedingt tun. In diesem Fall sammelt nämlich die Datenbank-Schnittstelle die Werte und sendet sie „in einem Rutsch“ zum Server. In Java sähe das ungefähr so aus:

        try (Connection con = DriverManager.getConnection();
             PreparedStatement insert = con.prepareStatement("INSERT INTO db_name (name, wert) VALUES(?,?)")
        {
            for (Item i : item_list) {
                insert.setString(1, i.getName());
                insert.setString(2, i.getValue());
                insert.addBatch();
            }
        
            insert.executeBatch();
        }
        

        Ich hatte bei einem node-Projekt von mir, einen ähnlichen Fall. Hier konnte jeweils ein Array für "key und value" übergeben werden und es wurde alles mit einem "Schreibzugriff" in die Datenbank geschrieben.

        Ich vermute, dass es eine Node-Bibliothek gibt, die genau diese Batch-Inserts kapselt. Ob es so etwas für .Net gibt, weiß ich leider nicht.

        Viele Grüße
        Robert