如何找到两个数据表之间的差异

时间:2019-02-05 20:34:11

标签: .net vb.net datatable

如何获取两个数据表之间的差异?

正在查看:How to compare 2 dataTables

我尝试过:

DataTable dataTable1; // Load with data
DataTable dataTable2; // Load with data (same schema)

var differences =
    dataTable1.AsEnumerable().Except(dataTable2.AsEnumerable(),
                                        DataRowComparer.Default);

return differences.Any() ? differences.CopyToDataTable() : new DataTable();

但是得到了:

"Public member 'Any' on type '<ExceptIterator>d__73(Of DataRow)' not found."

正在查看:How to get difference between two DataTables

我尝试过:

DataSet firstDsData = new DataSet();
DataSet secondDsData = new DataSet();
DataSet finalDsData = new DataSet();
DataSet DifferenceDataSet = new DataSet();
finalDsData.Merge(firstDsData);
finalDsData.AcceptChanges();
finalDsData.Merge(secondDsData);
DifferenceDataSet = finalDsData.GetChanges();

但是得到这个:

?DifferenceDataSet
Nothing

我知道有区别,因为我把它放在那里了。

我不希望链接表,因为该应用程序允许比较任何服务器和任何数据库。这样,我们可以根据需要轻松查看来自生产,测试和开发的数据。

还要注意,数据表是使用“ SELECT *”构建的,但最终将可以更改为选择某些字段。

是的,我可以遍历行和列,但这需要进行排序。并且由于可以使用任何表,因此我必须从SQL服务器提取PK数据,然后将其附加到sql。虽然并非不可能,但我希望有更好的方法。

我不希望使用LINQ,但不要停止发布LINQ解决方案,因为它们可能会帮助他人。

我的代码:

Public Function GetTable(ByVal strTable As String, ByVal strServer As String, ByVal strDatabase As String, ByVal strUser As String, ByVal strPassword As String) As DataTable

    Dim DT As DataTable


    Using Conn As New SqlConnection

        Conn.ConnectionString = [String].Format(mconSQLConnectString, strServer, strDatabase, strUser, strPassword, strDatabase)
        Conn.Open()

        DT = New DataTable

        Using cmd As SqlCommand = New SqlCommand("SELECT * FROM " & strTable, Conn)
            DT.Load(cmd.ExecuteReader(CommandBehavior.KeyInfo))
            DT.Load(cmd.ExecuteReader())
        End Using

    End Using

    Return DT

End Function



Private Sub Diff()

    Dim DT As DataTable
    Dim DTS As DataTable
    Dim DTT As DataTable
    Dim strTableName As String
    Dim TableNode As TreeNode


    mdictSource = New Dictionary(Of String, DataTable)
    mdictTarget = New Dictionary(Of String, DataTable)

    For Each TableNode In treSource.Nodes

        strTableName = TableNode.Text

        If TableNode.Checked Then
            DTS = DA.GetTable(strTableName, cboServerSource.Text, cboDatabaseSource.Text, txtUserSource.Text, txtPasswordSource.Text)
            mdictSource.Add(strTableName, DT)
            DTT = DA.GetTable(strTableName, cboServerTarget.Text, cboDatabaseTarget.Text, txtUserTarget.Text, txtPasswordTarget.Text)
            mdictTarget.Add(strTableName, DT)

            '''''''''''''''''''''''''''''''''''''
            'Method 1
            Dim differences
            differences = DTS.AsEnumerable().Except(DTT.AsEnumerable(), DataRowComparer.Default)

            If differences.Any() Then 'Public member 'Any' on type '<ExceptIterator>d__73(Of DataRow)' not found.
                DT = differences.CopyToDataTable
            End If
            '''''''''''''''''''''''''''''''''''''
            'Method 2
            Dim finalDsData = New DataSet
            Dim DifferenceDataSet As DataSet
            finalDsData.Merge(DTS)
            finalDsData.AcceptChanges()
            finalDsData.Merge(DTT)
            DifferenceDataSet = finalDsData.GetChanges()

        End If

    Next

End Sub

我知道这是有区别的,因为我创建了它:

?DTt.rows(11).Item(2)
"RFQs Issued" {String}
    String: "RFQs Issued"
?DTs.rows(11).Item(2)
"RFQs Issued!" {String}
    String: "RFQs Issued!"

只要获得表,pkey和字段,我都不在乎如何获得结果

编辑:查找不匹配的主键的解决方案。仍然需要检查数据类型,但是当此逻辑完成后,应该很容易地包含非主键列:

Private Sub Diff()

    Dim DGV As DataGridView
    Dim DT As DataTable = Nothing
    Dim DTS As DataTable
    Dim DTT As DataTable
    Dim strMessage As String
    Dim TP As TabPage
    Dim strTableName As String
    Dim TableNode As TreeNode


    mdictSource = New Dictionary(Of String, DataTable)
    mdictTarget = New Dictionary(Of String, DataTable)

    For Each TableNode In treSource.Nodes

        If TableNode.Checked Then

            strTableName = TableNode.Text
            DGV = New DataGridView
            DGV.AllowUserToAddRows = False
            DGV.Dock = DockStyle.Fill
            DGV.Name = "dgv" & strTableName
            DGV.Visible = True
            TP = New TabPage
            TP.Name = "tp" & strTableName
            TP.Text = strTableName
            TP.Controls.Add(DGV)
            tcTableResults.TabPages.Add(TP)

            DTS = DA.GetTable(strTableName, cboServerSource.Text, cboDatabaseSource.Text, txtUserSource.Text, txtPasswordSource.Text)
            mdictSource.Add(strTableName, DT)
            DTT = DA.GetTable(strTableName, cboServerTarget.Text, cboDatabaseTarget.Text, txtUserTarget.Text, txtPasswordTarget.Text)
            mdictTarget.Add(strTableName, DT)

            If PrimarykeyCountMatches(DTS, DTT) Then

                If PrimarykeysMatch(DGV, DTS, DTT) Then

                    If ColumnCountMatches(DTS, DTT) Then

                        If ColumnsMatch(DTS, DTT) Then

                            strMessage &= strTableName & " matches." & Environment.NewLine

                        Else

                        End If

                    Else

                        DisplayDiffPrimarykeys(DGV, DTS, DTT)

                    End If

                End If

            Else

                DisplayDiffPrimarykeys(DGV, DTS, DTT)

            End If

        End If

    Next

    If strMessage <> String.Empty Then
        MessageBox.Show(strMessage, "Results")
    End If

End Sub

Private Sub DisplayDiffColumns(DGV As DataGridView, DTS As DataTable, DTT As DataTable)



End Sub

Private Sub DisplayDiffPrimarykeys(DGV As DataGridView, DTS As DataTable, DTT As DataTable)

    Dim DC As DataGridViewColumn
    Dim DR As DataGridViewRow
    Dim intNewRow As Integer
    Dim intOffset As Integer
    Dim r As Integer

    For r = 0 To DTS.PrimaryKey.Length - 1
        DC = New DataGridViewTextBoxColumn
        DC.HeaderText = "Source: PKey" & (r + 1).ToString
        DC.ReadOnly = True
        DGV.Columns.Add(DC)
    Next
    For r = 0 To DTT.PrimaryKey.Length - 1
        DC = New DataGridViewTextBoxColumn
        DC.HeaderText = "Target: PKey" & (r + 1).ToString
        DC.ReadOnly = True
        DGV.Columns.Add(DC)
    Next
    intNewRow = DGV.Rows.Add()
    DR = DGV.Rows(intNewRow)
    DR.HeaderCell.Value = "PKeys"
    For r = 0 To DTS.PrimaryKey.Length - 1
        DR.Cells(r).Value = DTS.PrimaryKey(r)
    Next
    intOffset = r
    For r = 0 To DTT.PrimaryKey.Length - 1
        DR.Cells(r + intOffset).Value = DTT.PrimaryKey(r)
    Next

End Sub

Private Function PrimarykeyCountMatches(DTS As DataTable, DTT As DataTable) As Boolean

    Return DTS.PrimaryKey.Length = DTT.PrimaryKey.Length

End Function

Private Function PrimarykeysMatch(DGV As DataGridView, DTS As DataTable, DTT As DataTable) As Boolean

    Dim DC As DataGridViewColumn
    Dim lisDiffSource As List(Of Integer)
    Dim lisDiffTarget As List(Of Integer)
    Dim DR As DataGridViewRow
    Dim intIndex As Integer
    Dim bMatch As Boolean = False
    Dim intNewRow As Integer
    Dim PKeysSource As String()
    Dim PKeysTarget As String()
    Dim s As IOrderedEnumerable(Of DataColumn)
    Dim strSCaption As String
    Dim intSIndex As Integer
    Dim t As IOrderedEnumerable(Of DataColumn)
    Dim strTCaption As String
    Dim intTIndex As Integer

    s = DTS.PrimaryKey.OrderBy(Function(c) c.Caption)
    t = DTT.PrimaryKey.OrderBy(Function(c) c.Caption)


    lisDiffSource = New List(Of Integer)
    lisDiffTarget = New List(Of Integer)

    Do
        strSCaption = s(intSIndex).Caption.ToLower
        strTCaption = t(intTIndex).Caption.ToLower
        If strSCaption = strTCaption Then
            intSIndex += 1
            intTIndex += 1
        Else
            If strSCaption > strTCaption Then
                lisDiffTarget.Add(intTIndex)
                intTIndex += 1
            Else 'strSCaption < strTCaption
                lisDiffSource.Add(intSIndex)
                intSIndex += 1
            End If
        End If
    Loop While intSIndex < s.Count And intTIndex < t.Count


    While intSIndex < s.Count
        lisDiffSource.Add(intSIndex)
        intSIndex += 1
    End While

    While intTIndex < t.Count
        lisDiffTarget.Add(intTIndex)
        intTIndex += 1
    End While


    If lisDiffSource.Count = 0 And lisDiffTarget.Count = 0 Then
        bMatch = True
    Else

        DC = New DataGridViewTextBoxColumn
        DC.HeaderText = "Table"
        DC.ReadOnly = True
        DGV.Columns.Add(DC)
        DC = New DataGridViewTextBoxColumn
        DC.HeaderText = "Column"
        DC.ReadOnly = True
        DGV.Columns.Add(DC)
        DC = New DataGridViewTextBoxColumn
        DC.HeaderText = "Status"
        DC.ReadOnly = True
        DGV.Columns.Add(DC)

        For Each intIndex In lisDiffSource
            intNewRow = DGV.Rows.Add()
            DR = DGV.Rows(intNewRow)
            DR.Cells(0).Value = "Source"
            DR.Cells(1).Value = s(intIndex).Caption
            DR.Cells(2).Value = "Missing from Target"
        Next

        For Each intIndex In lisDiffTarget
            intNewRow = DGV.Rows.Add()
            DR = DGV.Rows(intNewRow)
            DR.Cells(0).Value = "Target"
            DR.Cells(1).Value = t(intIndex).Caption
            DR.Cells(2).Value = "Missing from Source"
        Next

    End If

    Return bMatch

End Function

Private Function ColumnCountMatches(DTS As DataTable, DTT As DataTable) As Boolean

    Return DTS.Columns.Count = DTT.Columns.Count

End Function

Private Function ColumnsMatch(DTS As DataTable, DTT As DataTable) As Boolean

    Return True

End Function

0 个答案:

没有答案