简体   繁体   English

在 vb.net 中使用 LINQ 排除连接

[英]Left excluding join using LINQ in vb.net

I have 2 DataTables:我有 2 个数据表:

TableA:

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

TableB:

id
--
2
5

I want to do a left excluding join between them: I want a DataTable object with all rows of TableA which have a corresponding id Column in TableB .我想在它们之间进行左排除连接:我想要一个 DataTable 对象,其中包含TableA所有行,这些行在TableB有一个相应的id列。

The result I want to have is this:我想要的结果是这样的:

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

I already have done this with for loops, but I think it could be simpler if I used LINQ.我已经用for循环做到了这一点,但我认为如果我使用 LINQ 会更简单。 The problem is that all I've tried doesn't work.问题是我尝试过的所有方法都不起作用。

How can I do this using LINQ?如何使用 LINQ 执行此操作?

The code with for loops is this: 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

An intro, to simplify the procedure (could be useful in other cases):介绍,以简化程序(在其他情况下可能有用):

  • Define the Columns data type explicitly:明确定义 Columns 数据类型:

     tableA.Columns.Add("id", GetType(Integer))
  • A phone number (code) is a string, not an integer (maybe phone is just an example, never mind):电话号码(代码)是一个字符串,而不是一个整数(也许phone只是一个例子,没关系):

     tableA.Columns.Add("phone", GetType(String))
  • A Row can be added using the overload that accepts a paramarray as object() .可以使用接受paramarray as object()的重载添加行。 It can be a sequence of values or an Object() array:它可以是一个值序列或一个 Object() 数组:

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

The System.Data.DataSetExtensions.dll assembly contains extension methods that allow to apply LINQ queries (which operate on IEnumerable<T> collections) to DataTables, using the AsEnumerable() extension method. System.Data.DataSetExtensions.dll程序集包含允许使用AsEnumerable()扩展方法将 LINQ 查询(对IEnumerable<T>集合进行操作)应用于数据表的扩展方法。
This assembly is usually included in new Projects, along with the System.Data assembly.此程序集通常与System.Data程序集一起包含在新项目中。 If it's not there, add a Project reference.如果不存在,请添加项目引用。

After that, the usual filter conditions ( Where() , Any() , All() etc.) can be used on the IEnumerable(Of DataRow) objects that the extension method generates.之后,可以在扩展方法生成的IEnumerable(Of DataRow)对象上使用通常的过滤条件( Where()Any()All()等)。

The IEnumerable results of the query ( EnumerableRowCollection<TRow> ) can be converted into a DataTable using the CopyToDataTable() extension method.查询的 IEnumerable 结果 ( EnumerableRowCollection<TRow> ) 可以使用CopyToDataTable()扩展方法转换为 DataTable。

  • We want to include Rows in the first DataTable Where() the id Column is not equal to Any() id Column of a second Datatable and create a new DataTable using this filter:我们希望在第一个 DataTable 中包含 Rows, Where() id Column 不等于第二个 Datatable 的Any() id Column 并使用此过滤器创建一个新的 DataTable:
    (of course you can construct a filter that applies the same conditions in a different way) (当然,您可以构建一个以不同方式应用相同条件的过滤器)
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

In addition to Jimi's very good answer , it is also possible to use the LINQ Enumerable.Except method.除了Jimi 非常好的回答,还可以使用 LINQ Enumerable.Except方法。 It just needs a helper class to compare the appropriate element(s) from each datatable's rows.它只需要一个辅助类来比较每个数据表行中的适当元素。

Shamelessly using parts of the code from that answer:无耻地使用该答案中的部分代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM