【问题标题】:in c# I would like to compare datatables在c#中我想比较数据表
【发布时间】:2011-04-11 19:57:00
【问题描述】:

我想比较两个数据表。我需要知道的是 contents 不同的是或否。谁能推荐一种快速比较它们的方法?

我想知道内容(数据)是否不同

【问题讨论】:

  • 你想比较什么?数据库结构?还是数据库结构和数据?
  • 只有当两个表具有相同的列名、列数据类型和相同的行顺序时,它们才被认为是相同的吗?
  • 是什么阻止您比较表格?

标签: c# .net-2.0


【解决方案1】:

我以为我最近编写的代码解决了这个问题,但发现我正在做的事情并没有真正起作用。所以我不得不重新审视这个,最近看到了这篇文章。

我之前的做法是在两个内存流上都使用 WriteXml,并一次通过两个字符串运行 256 个字符来寻找差异。虽然这在大部分时间都有效,但我开始意识到我的行和列不一定与另一个的顺序相同,尽管是从同一个过程调用的。

所以我通过比较每一行的哈希码来重新设计它。尽管列和行的顺序不同,这最终使我能够比较所有内容。

首先,我尝试对诸如行数和列数之类的差异进行一些快速测试,然后确保第一个表中的列存在于第二个表中。如果我们做到了这一点,那么就该对哈希码进行广泛的测试了。我原以为它可能需要很长时间才能运行,但结果并没有我想象的那么糟糕。

我知道代码是用 Visual Basic 编写的,很抱歉,因为我看到您正在寻找 C#,但我确信转换它很容易。

虽然这个线程很旧,但我希望这段代码可以帮助任何想要做同样事情的人。 它肯定胜过逐个单元格的检查。

''' <summary>
''' Compares the data between both datatables to check if they have the same content or not.
''' Columns and Rows do not need to be in the same order as the other, just that the data in any of them need to be the same.
''' </summary>
''' <param name="dtbl_a">The first datatable to compare.</param>
''' <param name="dtbl_b">The second datatable to compare.</param>
''' <param name="debug">For troubleshooting and checking the function is working correctly.  Typically this is 'false'.</param>
''' <returns>Boolean:  False if the data between the two datatables are different, True if the data between them are the same.</returns>
''' <remarks></remarks>
Public Function dataTablesAreEqual _
                                  (ByRef dtbl_a As System.Data.DataTable, _
                                   ByRef dtbl_b As System.Data.DataTable, _
                                   ByVal debug As System.Boolean) As System.Boolean

    Dim recordCount(1) As System.Int32
    Dim columnCount(1) As System.Int32
    Dim length(1) As System.Int32
    Dim dtbl_check(1) As System.Data.DataTable
    Dim debugMessage As New System.Text.StringBuilder
    Dim errorMessage As System.String
    Dim result As System.Boolean

    Try
        If debug = True Then
            debugMessage.AppendLine("checking:  priliminary (and easy to see) tests being done...")
            debugMessage.AppendLine("checking:  what is nothing and what is not nothing.")

        End If

        If dtbl_a Is Nothing And dtbl_b Is Nothing Then
            result = True

            If debug = True Then
                debugMessage.AppendLine("decision:  " & result.ToString & "!")
                debugMessage.AppendLine("  reason:  dtbl_a Is Nothing And dtbl_b Is Nothing.")

            End If

            Return result

        ElseIf dtbl_a Is Nothing And dtbl_b IsNot Nothing Then
            result = False

            If debug = True Then
                debugMessage.AppendLine("decision:  " & result.ToString & "!")
                debugMessage.AppendLine("  reason:  dtbl_a Is Nothing And dtbl_b IsNot Nothing.")

            End If

            Return result

        ElseIf dtbl_a IsNot Nothing And dtbl_b Is Nothing Then
            result = False

            If debug = True Then
                debugMessage.AppendLine("decision:  " & result.ToString & "!")
                debugMessage.AppendLine("  reason:  dtbl_a IsNot Nothing And dtbl_b Is Nothing.")

            End If

            Return result

        End If

        If debug = True Then
            debugMessage.AppendLine("checking:  differences in row and column counts.")

        End If

        recordCount(0) = dtbl_a.Rows.Count
        columnCount(0) = dtbl_a.Columns.Count

        recordCount(1) = dtbl_b.Rows.Count
        columnCount(1) = dtbl_b.Columns.Count

        If recordCount(0) <> recordCount(1) Then
            result = False

            If debug = True Then
                debugMessage.AppendLine("decision:  " & result.ToString & "!")
                debugMessage.AppendLine("  reason:  the # of rows are different.")
                debugMessage.AppendLine("  reason:  dtbl_a has " & recordCount(0) & " record(s) and dtbl_b has " & recordCount(1) & " record(s).")

            End If

            Return result

        ElseIf columnCount(0) <> columnCount(1) Then
            result = False

            If debug = True Then
                debugMessage.AppendLine("decision:  " & result.ToString & "!")
                debugMessage.AppendLine("  reason:  the # of columns are different.")
                debugMessage.AppendLine("  reason:  dtbl_a has " & columnCount(0) & " column(s) and dtbl_b has " & columnCount(1) & " column(s).")

            End If

            Return result

        End If

        Dim lowerBound_Records As System.Int32
        Dim upperBound_Records As System.Int32
        Dim current_Record As System.Int32
        Dim lowerBound_Columns As System.Int32
        Dim upperBound_Columns As System.Int32
        Dim current_Column As System.Int32

        lowerBound_Records = 0
        upperBound_Records = (recordCount(0) - 1)
        lowerBound_Columns = 0
        upperBound_Columns = (columnCount(0) - 1)

        If debug = True Then
            debugMessage.AppendLine("checking:  columns exist in both tables (can be in different order).")

        End If

        For current_Column = lowerBound_Columns To upperBound_Columns
            Dim columnName As System.String
            Dim exists As System.Boolean

            columnName = dtbl_a.Columns(current_Column).ColumnName
            exists = dtbl_b.Columns.Contains(columnName)

            If exists = False Then
                result = False

                If debug = True Then
                    debugMessage.AppendLine("decision:  " & result.ToString & "!")
                    debugMessage.AppendLine("  reason:  the column [" & columnName & "] in dtbl_a does NOT EXIST in dtbl_b.")

                End If

                Return result

            End If

        Next current_Column

        dtbl_check(0) = New System.Data.DataTable
        dtbl_check(0).Columns.Add("index", GetType(System.Int32))
        dtbl_check(0).Columns.Add("hashCode", GetType(System.Int32))
        dtbl_check(0).Columns.Add("checked", GetType(System.DateTime))

        If debug = True Then
            dtbl_check(0).Columns.Add("dataRow", GetType(System.String))

        End If

        dtbl_check(0).PrimaryKey = New System.Data.DataColumn() {dtbl_check(0).Columns("index"), dtbl_check(0).Columns("hashCode")}
        dtbl_check(1) = dtbl_check(0).Copy

        If debug = True Then
            debugMessage.AppendLine("checking:  going for the extensive data content check (rows can be in different order).")
            debugMessage.AppendLine("checking:  getting hashcodes from dtbl_a started:  " & System.DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.ffff"))

        End If

        For current_Record = lowerBound_Records To upperBound_Records
            Dim dataRow_check As New System.Text.StringBuilder

            dataRow_check.Append("<dataRow>")
            For current_Column = lowerBound_Columns To upperBound_Columns
                Dim columnName As System.String
                Dim valueIsNull As System.Boolean
                Dim value As System.String

                columnName = dtbl_a.Columns(current_Column).ColumnName
                valueIsNull = System.Convert.IsDBNull(dtbl_a.Rows(current_Record).Item(columnName))

                If valueIsNull = False Then
                    value = dtbl_a.Rows(current_Record).Item(columnName).ToString

                Else
                    value = "[null]"

                End If

                dataRow_check.Append("<" & columnName & ">" & value & "</" & columnName & ">")

            Next current_Column

            dataRow_check.Append("</dataRow>")

            Dim newRow As System.Data.DataRow = dtbl_check(0).NewRow
            Call newRow.BeginEdit()
            newRow.Item("index") = current_Record
            newRow.Item("hashCode") = dataRow_check.ToString.GetHashCode
            newRow.Item("checked") = System.Convert.DBNull

            If debug = True Then
                newRow.Item("dataRow") = dataRow_check.ToString

            End If

            Call newRow.EndEdit()
            Call dtbl_check(0).Rows.Add(newRow)
            dataRow_check = Nothing

        Next current_Record

        If debug = True Then
            debugMessage.AppendLine("checking:  finished hashcodes for dtbl_a ended:  " & System.DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.ffff"))
            debugMessage.AppendLine("checking:  getting hashcodes from dtbl_b and cross checking using dtbl_a started:  " & System.DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.ffff"))

        End If

        For current_Record = lowerBound_Records To upperBound_Records
            Dim dataRow_check As New System.Text.StringBuilder
            Dim checked As System.DateTime

            checked = System.DateTime.Now
            dataRow_check.Append("<dataRow>")

            For current_Column = lowerBound_Columns To upperBound_Columns
                Dim columnName As System.String
                Dim valueIsNull As System.Boolean
                Dim value As System.String

                columnName = dtbl_a.Columns(current_Column).ColumnName

                Try
                    valueIsNull = System.Convert.IsDBNull(dtbl_b.Rows(current_Record).Item(columnName))

                Catch exc As System.Exception
                    result = False
                    Return result

                End Try

                If valueIsNull = False Then
                    value = dtbl_b.Rows(current_Record).Item(columnName).ToString

                Else
                    value = "[null]"

                End If

                dataRow_check.Append("<" & columnName & ">" & value & "</" & columnName & ">")

            Next current_Column

            dataRow_check.Append("</dataRow>")

            Dim selectQuery As System.String
            Dim dataRows() As System.Data.DataRow
            Dim hashCode As System.Int32

            hashCode = dataRow_check.ToString.GetHashCode
            selectQuery = "[hashCode] = " & hashCode.ToString & " and [checked] is null "
            dataRows = dtbl_check(0).Select(selectQuery)

            If dataRows.Length > 0 Then
                Call dataRows(0).BeginEdit()
                dataRows(0).Item("checked") = checked
                Call dataRows(0).EndEdit()

                Dim newRow As System.Data.DataRow = dtbl_check(1).NewRow
                Call newRow.BeginEdit()
                newRow.Item("index") = dataRows(0).Item("index")
                newRow.Item("hashCode") = dataRows(0).Item("hashCode")
                newRow.Item("checked") = checked

                If debug = True Then
                    newRow.Item("dataRow") = dataRow_check.ToString

                End If

                Call newRow.EndEdit()
                Call dtbl_check(1).Rows.Add(newRow)

            Else
                result = False

                If debug = True Then
                    debugMessage.AppendLine("checking:  finished hashcodes for dtbl_b ended:  " & System.DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.ffff"))
                    debugMessage.AppendLine("decision:  " & result.ToString & "!")
                    debugMessage.AppendLine("  reason:  a row in dtbl_b doesn't match up in dtbl_a (either due to changes or didn't exist).")
                    debugMessage.AppendLine(" dataRow:  " & dataRow_check.ToString)

                End If

                dataRow_check = Nothing

                Return result

            End If

            dataRows = Nothing
            dataRow_check = Nothing

        Next current_Record

        result = True

        If debug = True Then
            debugMessage.AppendLine("checking:  finished hashcodes for dtbl_b ended:  " & System.DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.ffff"))
            debugMessage.AppendLine("decision:  " & result.ToString & "!")
            debugMessage.AppendLine("  reason:  all " & recordCount(0) & " record(s) match up between both tables.")

        End If

    Catch exc_Exception As System.Exception
        errorMessage = exc_Exception.Message
        result = False

        If debug = True Then
            debugMessage.AppendLine("exception in dataTablesAreEqual!")
            debugMessage.AppendLine("decision:  " & result.ToString & "!")
            debugMessage.AppendLine(" message:  " & errorMessage)

        End If

    Finally
        If dtbl_check(1) IsNot Nothing Then
            Call dtbl_check(1).Clear()
            dtbl_check(1) = Nothing

        End If

        If dtbl_check(0) IsNot Nothing Then
            Call dtbl_check(0).Clear()
            dtbl_check(0) = Nothing

        End If

        If debug = True Then
            Call System.Diagnostics.Debug.WriteLine(debugMessage.ToString)

        End If

        debugMessage = Nothing

    End Try

    Return result

End Function

【讨论】:

    【解决方案2】:

    假设表具有相同的结构(列名和数据类型)

    static bool AreTablesEqual(DataTable t1, DataTable t2)
    {
        // If the number of rows is different, no need to compare the data
        if (t1.Rows.Count != t2.Rows.Count)
            return false;
    
        for (int i = 0; i < t1.Rows.Count; i++)
        {
            foreach(DataColumn col in t1.Columns)
            {
                if (!Equals(t1.Rows[i][col.ColumnName], t2.Rows[i][col.ColumnName]))
                    return false;
            }
        }
        return true;
    }
    

    【讨论】:

    • 两个表的相等不应该取决于两个表之间匹配的行的顺序。 (尽管这对于 OP 来说可能是可以接受的。如果没有更多信息就无法判断。)无论如何,只需在调用您的方法之前对表进行唯一排序,这个问题就解决了。
    • 你不需要假设相同的结构,答案stackoverflow.com/a/7518099/52277也比较列数
    • 两个问题:...3,C# 是否得到了更新,还是我使用了错误的库? 1) 当我使用 col.Name 时,它​​实际上给了我 col.ColumnName 2) 当尝试使用 t1[i] 时,我收到错误“无法使用 [] 将索引应用于“System.Data.DataTable”类型的表达式
    • @Macchtyn,不,这些是我的代码中的错误......当你发布代码甚至没有尝试编译它时会发生这种情况;)。我会解决的。
    【解决方案3】:

    我用过

        bool AreEqual(DataTable d1, DataTable d2)
        {
            if (d1.Rows.Count != d2.Rows.Count)
                return false;
            for (int i = 0; i < d1.Rows.Count; i++)
            {
                var d1Row = d1.Rows[i];
                var d2Row = d2.Rows[i];
                for (int j = 0; j < d1Row.ItemArray.Count(); j++)
                {
                    if (!d1Row[j].Equals(d2Row[j]))
                       return false;
                }
            }
    
            return true;
        }
    

    【讨论】:

      【解决方案4】:

      我会编写一个覆盖 object.Equals 的扩展方法,获取两个 DataTable 并将它们与您想要的任何标准进行比较。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-27
      • 2020-12-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多