【问题标题】:Left excluding join using LINQ in vb.net在 vb.net 中使用 LINQ 排除连接
【发布时间】:2020-11-16 17:24:11
【问题描述】:

我有 2 个数据表:

TableA:

id | name | phone
-----------------
1  | Paul | 8523      
2  | John | 5217     
3  | Stan | 5021

TableB:

id
--
2
5

我想在它们之间进行左排除连接:我想要一个 DataTable 对象,其中包含TableA 的所有行,在TableB 中具有相应的id 列。

我想要的结果是这样的:

id | name | phone
-----------------
1  | Paul | 8523
3  | Stan | 5021

我已经使用for 循环完成了这项工作,但我认为如果我使用 LINQ 会更简单。问题是我尝试过的所有方法都不起作用。

如何使用 LINQ 做到这一点?

带有for 循环的代码是这样的:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim table = LeftExcludingJoin()

    If table IsNot Nothing Then
        For Each row As DataRow In table.Rows
            Console.WriteLine(row.Item("id") & " - " & row.Item("name") & " - " & row.Item("phone"))
        Next
    End If

End Sub

Private Function LeftExcludingJoin() As DataTable
    Dim tableA As New DataTable
    tableA.Columns.Add("id")
    tableA.Columns.Add("name")
    tableA.Columns.Add("phone")

    Dim tableB As New DataTable
    tableB.Columns.Add("id")

    'Rows for tableA
    Dim rowA1 As DataRow = tableA.NewRow
    rowA1.Item("id") = 1
    rowA1.Item("name") = "Paul"
    rowA1.Item("phone") = 8523
    tableA.Rows.Add(rowA1)

    Dim rowA2 As DataRow = tableA.NewRow
    rowA2.Item("id") = 2
    rowA2.Item("name") = "John"
    rowA2.Item("phone") = 5217
    tableA.Rows.Add(rowA2)

    Dim rowA3 As DataRow = tableA.NewRow
    rowA3.Item("id") = 3
    rowA3.Item("name") = "Stan"
    rowA3.Item("phone") = 5021
    tableA.Rows.Add(rowA3)

    'Rows for tableB
    Dim rowB1 As DataRow = tableB.NewRow
    rowB1.Item("id") = 2
    tableB.Rows.Add(rowB1)

    Dim rowB2 As DataRow = tableB.NewRow
    rowB2.Item("id") = 5
    tableB.Rows.Add(rowB2)

    'Get rows in A which are not in B
    Dim tableResult = tableA.Clone

    If tableA IsNot Nothing Then
        If tableB IsNot Nothing Then
            For Each rowA As DataRow In tableA.Rows
                Dim coincidence As Boolean = False
                For Each rowB As DataRow In tableB.Rows
                    If rowA.Item("id") = rowB.Item("id") Then
                        coincidence = True
                        Exit For
                    End If
                Next
                If coincidence = False Then
                    Dim newResultRow As DataRow = tableResult.NewRow
                    newResultRow.Item("id") = rowA.Item("id")
                    newResultRow.Item("name") = rowA.Item("name")
                    newResultRow.Item("phone") = rowA.Item("phone")
                    tableResult.Rows.Add(newResultRow)
                End If
            Next
        Else
            'All tableA values are part of the result because tableB is Nothing.
            Return tableA
        End If
        Return tableResult
    Else
        Return Nothing
    End If
End Function

【问题讨论】:

    标签: .net vb.net linq datatable


    【解决方案1】:

    介绍,以简化程序(在其他情况下可能有用):

    • 明确定义 Columns 数据类型:

      tableA.Columns.Add("id", GetType(Integer))
      
    • 电话号码(代码)是一个字符串,而不是一个整数(也许phone 只是一个例子,没关系):

      tableA.Columns.Add("phone", GetType(String))
      
    • 可以使用接受paramarray as object() 的重载来添加行。它可以是一个值序列或一个 Object() 数组:

      tableA.Rows.Add(1, "Paul", "8523")
      

    System.Data.DataSetExtensions.dll 程序集包含扩展方法,允许使用 AsEnumerable() 扩展方法将 LINQ 查询(对 IEnumerable<T> 集合进行操作)应用于 DataTables。
    此程序集通常与 System.Data 程序集一起包含在新项目中。如果不存在,请添加项目引用。

    之后,可以在扩展方法生成的IEnumerable(Of DataRow)对象上使用常用的过滤条件(Where()Any()All()等)。

    查询的 IEnumerable 结果 (EnumerableRowCollection<TRow>) 可以使用CopyToDataTable() 扩展方法转换为 DataTable。

    • 我们希望在第一个 DataTable 中包含行 Where() id Column 不等于 Any() id 第二个 Datatable 的列,并使用此过滤器创建一个新的 DataTable:
      (当然,您可以构建一个过滤器,以不同的方式应用相同的条件)
    Private Function LeftExcludingJoin() As DataTable
        Dim tableA As New DataTable("TableA")
        tableA.Columns.Add("id", GetType(Integer))
        tableA.Columns.Add("name", GetType(String))
        tableA.Columns.Add("phone", GetType(String))
    
        tableA.Rows.Add(1, "Paul", "8523")
        tableA.Rows.Add(2, "John", "5217")
        tableA.Rows.Add(3, "Stan", "5021")
    
        Dim tableB As New DataTable("TableB")
        tableB.Columns.Add("id", GetType(Integer))
    
        tableB.Rows.Add(2)
        tableB.Rows.Add(5)
    
        Dim rows = tableA.AsEnumerable().
            Where(Function(drA) Not tableB.AsEnumerable().
                Any(Function(drB) CInt(drB("id")) = CInt(drA("id"))))
    
        Return If(rows.Count() = 0, Nothing, rows.CopyToDataTable())
    End Function
    

    【讨论】:

      【解决方案2】:

      除了Jimi's very good answer,还可以使用LINQEnumerable.Except方法。它只需要一个帮助类来比较每个数据表行中的适当元素。

      无耻地使用该答案中的部分代码:

      Public Class IdComparer
          Implements IEqualityComparer(Of DataRow)
      
          Public Function Equals1(ByVal x As DataRow, ByVal y As DataRow) As Boolean Implements IEqualityComparer(Of DataRow).Equals
              If x Is y Then
                  Return True
              End If
      
              If x Is Nothing OrElse y Is Nothing Then
                  Return False
              End If
      
              Return (x.Field(Of Integer)("id") = y.Field(Of Integer)("id"))
      
          End Function
      
          Public Function GetHashCode1(ByVal dr As DataRow) As Integer Implements IEqualityComparer(Of DataRow).GetHashCode
              Return If(dr Is Nothing, 0, dr.Field(Of Integer)("id").GetHashCode())
      
          End Function
      
      End Class
      
      Private Function LeftExcludingJoin() As DataTable
          Dim tableA As New DataTable("TableA")
          tableA.Columns.Add("id", GetType(Integer))
          tableA.Columns.Add("name", GetType(String))
          tableA.Columns.Add("phone", GetType(String))
      
          tableA.Rows.Add(1, "Paul", "8523")
          tableA.Rows.Add(2, "John", "5217")
          tableA.Rows.Add(3, "Stan", "5021")
      
          Dim tableB As New DataTable("TableB")
          tableB.Columns.Add("id", GetType(Integer))
      
          tableB.Rows.Add(2)
          tableB.Rows.Add(5)
      
          Dim q = tableA.AsEnumerable().Except(tableB.AsEnumerable(), New IdComparer())
      
          Return If(q.Count = 0, Nothing, q.CopyToDataTable())
      
      End Function
      

      【讨论】:

      • 也谢谢。两个版本比我所做的一切都容易。这就是我想要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多