Announcement

Collapse
No announcement yet.

SQL Tabelle mit Dataset vergleichen

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    PK = PrimaryKey.

    Ich stolpere gerade darüber, dass die ID vermutlich nicht geeignet ist für den Vergleich zwischen "vorhandenem" und "neuem" Eintrag. Aber das hängt natürlich von der Datenstruktur und der Art, wie der PK erzeugt wird, ab.

    Jürgen

    Comment


    • #17
      Hallo Jürgen,

      jeder Artikel hat eine eigene ID, die aufsteigend vergeben wird.

      Wie kann ich jetzt nach dem PK suchen, und diese vergleichen? Und wenn ich einen Unterschied gefunden habe, wie überschreibe ich diesen dann?

      Ich habe zwar schon öfter Daten aus der Datenbank herunter geladen, aber nie darauf geschrieben, und schon gar nicht updatet...

      Wäre schön, wenn ihr ein kurzes Codeschnippsel schicken könntet, aus dem ich mir dann meinen Syntax bauen kann..

      Vielen, Vielen Dank im Vorraus.

      Gruß

      Thors Hamster

      Comment


      • #18
        Hallo Thors,

        Originally posted by Thors.Hamster View Post
        jeder Artikel hat eine eigene ID, die aufsteigend vergeben wird.
        Ist die ID in der SQL-Datenbank identisch mit der in den Importdaten? Wenn ja, dann widerspricht das zwar den Bedingungen, die eine ID erfüllen sollte (eine ID hat nur die Aufgabe der Identifizierung eines Datensatzes innerhalb der Tabelle, aber keine inhaltliche Bedeutung), erleichtert dir aber den Vergleich. Wenn nein, dann musst du ein anderes Vergleichsmerkmal (z.B. die exakte Bezeichnung des Artikels oder die Artikelnummer, die "nach außen" vergeben wird) suchen.

        Wie kann ich jetzt nach dem PK suchen, und diese vergleichen?
        Wie fast immer gibt es mehrere Möglichkeiten. Was sinnvoll ist, hängt z.B. auch von der Datenmenge ab: 20 Änderungen bei 100.000 Artikeln werden anders geprüft als 10.000 Änderungen bei 15.000 Artikeln.

        Du kannst zu jeder einzelnen ID der Importdaten den passenden Eintrag aus der SQL-DB per SqlDataReader holen und vergleichen. (Das geht mit einem SELECT ... WHERE ID IN (...) auch in Blöcken, aber das IN geht dann nicht mit Parametern.)

        Oder du holst alle Daten per SqlDataAdapter.Fill in eine DataTable (siehe mein ursprünglicher Vorschlag) und vergleichst jede Zeile der Import-DataTable mit der entsprechenden Zeile der DB-DataTable. Sinnvollerweise gehören jeweils zum SELECT ein ORDER BY ID; alternativ kannst du per DataTable.Select (wie ebenfalls schon erwähnt) eine bestimmte DataRow nach der ID (oder der passenden Identifikation) suchen.

        Für den Vergleich musst du alle Spalten, die dich interessieren, einzeln vergleichen.

        Und wenn ich einen Unterschied gefunden habe, wie überschreibe ich diesen dann?
        Du kannst dir eine weitere DataTable erstellen, die nur noch die Unterschiede enthält (dann jeweils mit der ganzen Zeile, die per ImportRow kopiert werden kann), oder die Änderungen manuell von ds.Tables("ImportTable").Rows(aktuell).Items("Spal tenname") nach ds.Tables("SqlTable").Rows(gefunden).Items("Spalte nname") zuweisen.

        Wichtig ist, dass du (wegen des nächsten Schritts) alle gewünschten Änderungen in einer DataTable hast. Ob diese nur die geänderten Zeilen enthält oder alle Artikel, ist nicht ganz so wichtig; das kann durch die Art des Speicherns gesteuert werden.

        Ich habe zwar schon öfter Daten aus der Datenbank herunter geladen, aber nie darauf geschrieben, und schon gar nicht updatet...
        Das ist fast das einfachste. Zuerst der SQL-Befehl:
        Code:
        UPDATE (Tabellenname)
           SET ColumnName1 = @Param1,
               ColumnName2 = @Param2 usw.
          WHERE ID = @ID;
        Dieser wird (wie ich schon oben geschrieben) entweder innerhalb einer SqlTransaction einzeln über eine Schleife per SqlCommand.ExecuteNonQuery an die DB geschickt oder als UpdateCommand in einen DbDataAdapter eingebunden. (Letzteres erfordert weitere Vorarbeiten, Ersteres ist u.U. zu langsam, deshalb unbedingt Transaktion benutzen.) Zum SqlCommand gehören Parameter, z.B.:
        Code:
        SqlCommand cmd = new SqlCommand("Befehlstext, s.o.")
        cmd.Parameters.Add("@ID", SqlDbType.Int)
        cmd.Parameters.Add("@Param1", SqlDbType.VarChar, 30)  // oder so usw.
        Und in der Schleife werden jeweils die Werte zugeordnet, etwa so:
        Code:
        cmd.Parameters("@ID").Value = ds.Tables(0).Rows(0).Items("ID")
        Zu den Parametern siehe [Artikelserie] Parameter von SQL Befehlen.

        Kann es sein, dass du dich noch nie grundlegend mit der DB-Anbindung unter ADO.NET befasst hast? Dann solltest du dich mit OpenBook VB 2008 ab Kapitel 23 befassen.

        Gruß Jürgen

        Comment


        • #19
          Um auf die ursprüngliche Frage zurückzukommen:
          vorausgesetzt, die beiden DataTable-Objekte haben den gleichen Aufbau, anhand der Spalte keyColumn mit Namen keyColumnName können die Datenzeilen eindeutig einander zugeordnet werden (keine Duplikate, keine Nullwerte), dann läßt sich nachstehender Algorithmus einsetzen. Die Verwendung eines BinaryFormatters ist nicht zwingend. Man könnte den Zeileninhalt auch spaltenweise selbst vergleichen, müßte dabei lediglich den DataType der Spalte berücksichtigen. Eine Beschränkung auf die gebräuchlichen Datentypen einer Datenbank DateTime bzw. Date, Boolean, Integer, Long, Double, Byte() und String werden im Allgemeinen reichen.

          [highlight=vbnet]
          ''' <summary>
          ''' Gleicht dtChanges mit dtOriginal ab
          ''' </summary>
          ''' <param name="dtChanges"></param>
          ''' <param name="dtOriginal"></param>
          ''' <param name="keyColumnName"></param>
          ''' <remarks>
          ''' verwendet Serialisierung, um Zeilenvergleich durchzuführen
          ''' </remarks>
          Private Sub Merge(ByVal dtChanges As DataTable, ByVal dtOriginal As DataTable, _
          ByVal keyColumnName As String)
          'bei Verwendung eines BinaryFormatters
          Dim dtSerialize As DataTable = dtChanges.Clone
          Dim bf As New System.Runtime.Serialization.Formatters.Binary.Bin aryFormatter
          Dim bChanges As Byte()
          Dim bOriginal As Byte()
          Dim rows As New System.Collections.Generic.Dictionary(Of Object, DataRow)
          Dim column As DataColumn

          'alle Originalzeilen zum schnellen Wiederfinden auflisten
          column = dtOriginal.Columns(keyColumnName)
          For Each row As DataRow In dtOriginal.Rows
          rows.Add(row(column), row)
          Next

          'Zeilen prüfen
          column = dtChanges.Columns(keyColumnName)
          For Each row As DataRow In dtChanges.Rows
          If Validate(row) Then
          Dim key As Object = row(column)

          If rows.ContainsKey(key) Then
          'Original serialisieren
          dtSerialize.Rows.Clear()
          dtSerialize.LoadDataRow(rows(key).ItemArray, True)
          Using ms As New IO.MemoryStream
          bf.Serialize(ms, dtSerialize)
          bOriginal = ms.ToArray
          End Using

          'aktuelle Zeile serialisieren
          dtSerialize.Rows(0).ItemArray = row.ItemArray
          dtSerialize.Rows(0).AcceptChanges()
          Using ms As New IO.MemoryStream
          bf.Serialize(ms, dtSerialize)
          bChanges = ms.ToArray
          End Using

          If Equals(bOriginal, bChanges) Then
          'Zeile unverändert
          row.AcceptChanges()
          Else
          'Zeile geändert
          row.SetModified()
          End If

          'Originalzeile verarbeitet
          rows.Remove(key)
          Else
          'Zeile ist neu
          row.SetAdded()
          End If
          Else
          'Validierung fehlgeschlagen => Zeile als unverändert markieren
          row.AcceptChanges()
          End If
          Next

          'gelöschte Zeilen
          For Each de As System.Collections.Generic.KeyValuePair(Of Object, DataRow) In rows
          dtChanges.LoadDataRow(de.Value.ItemArray, True).Delete()
          Next
          End Sub
          Private Overloads Function Equals(ByVal buffer1 As Byte(), ByVal buffer2 As Byte()) As Boolean
          'Grundvoraussetzung für Gleichheit
          If buffer1.Length = buffer2.Length Then
          For index As Integer = 0 To buffer1.Length - 1
          If buffer1(index) <> buffer2(index) Then
          Return False
          End If
          Next

          Return True
          End If

          Return False
          End Function
          Private Overloads Function Validate(ByVal row As DataRow) As Boolean
          'hier sind eigene Ideen gefragt
          Return True
          End Function
          [/highlight]

          Comment


          • #20
            Hallo Jürgen, Hallo akk,

            vielen Dank für eure Antworten, werde es demnächst mal ausprobieren.
            Klingt aber schonmal sehr vielversprechend.

            Vielen, Vielen Dank.

            Gruß

            Thors Hamster

            Comment

            Working...
            X