[英]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。
Where()
the id
Column is not equal to Any()
id
Column of a second Datatable and create a new DataTable using this filter:Where()
id
Column 不等于第二个 Datatable 的Any()
id
Column 并使用此过滤器创建一个新的 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
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.