使用LINQ在datatable和提交的数组之间执行LEFT JOIN

时间:2016-01-08 08:36:43

标签: linq linq-to-sql datatable

我在SQL服务器中有一个大表(下面显示的'ManyColSQLTable'示例),我希望在日期和时间样本上执行LEFT JOIN('myDateTimes'示例同时包含Date和Time列,并且可能包含不在ManyColSQLTable中的日期和时间。对于myDateTimes作为SQL服务器中的单独表存在的情况,下面的SQL代码正确地执行了此操作,但我想使用vb.net和LINQ动态地使用提交的myDateTimes数组执行此操作。

1)使用SQLDataAdapter将ManyColSQLTable拉入vb.net DataTable,然后使用LINQ与提交的myDateTimes一起编写LEFT JOIN。如何使用LINQ语法然后使用myDateTimes执行LEFT JOIN?请注意,myDateTimes被输入为vb.net数组。

2)或者,在包含MyDateTimes数组数据的数据库中创建临时表(如何操作?),然后使用下面的TSQL语法执行连接,然后将结果拉回到vb中会更有效吗? net(例如,使用SQLDataAdapter作为DataTable)。

SQL Server中的ManyColSQLTable示例:

        Date        Time        EURUSD  USDJPY  GBPUSD  USDCAD
    1   2009-12-02  21:30:00    NULL    NULL    NULL    NULL
    2   2009-12-02  22:00:00    1.5047  87.4300 1.6636  1.0511
    3   2009-12-02  22:30:00    1.5048  87.6100 1.6640  1.0512
    4   2009-12-02  23:00:00    1.5061  87.5500 1.6652  1.0496
    5   2009-12-02  23:30:00    1.5067  87.6400 1.6661  1.0493
    ... ...         00:00:00    ...     ...     ...     ...
    108 2009-12-20  04:30:00    1.5088  86.7100 1.6756  1.0393

以下适用于SQL Management Studio:

SELECT  distinct *
 FROM [dbo].myDateTimes as j
 LEFT JOIN ManyColSQLTable ON 
 (ManyColSQLTable.Date = j.date and ManyColSQLTable.time = j.time) 
 Order by j.date desc , j.time desc

我尝试使用LINQ(语法显然是错误的 - 如何解决?):

Sub JoinExample()
        Dim myDateTimes() As Date = {#6/19/2013 12:30:00 AM#, #12/3/2009 3:00:00 AM#}
        Dim tbl As DataTable = FillDataTable()

        '  I'm stuck from this point on.  How get the syntax correct?
        Dim rsltTbl = From dt In myDateTimes
        Join [Date] In tbl.AsEnumerable() On
        dt Equals tbl.Columns([Date]) And dt.TimeOfDay Equals tbl.Columns([Time])
        Select tbl.Rows()
    End Sub

2 个答案:

答案 0 :(得分:1)

<强>被修改

试试这个例子:

Module StartupModule

    Sub Main()
        ' Mock data table.
        Dim dt As New DataTable
        dt.Columns.Add("Id", GetType(Integer))
        dt.Columns.Add("Date", GetType(DateTime))
        dt.Columns.Add("Description", GetType(String))
        dt.Columns.Add("Amount", GetType(Decimal))


        Dim addRow As Action(Of Integer, DateTime, String, Decimal?) = Sub(id, [date], description, amount)
                                                                           Dim r = dt.NewRow
                                                                           r.SetField("Id", id)
                                                                           r.SetField("Date", [date])

                                                                           If (Not String.IsNullOrEmpty(description)) Then
                                                                               r.SetField("Description", description)
                                                                           End If

                                                                           If (amount.HasValue) Then
                                                                               r.SetField("Amount", amount.Value)
                                                                           End If

                                                                           dt.Rows.Add(r)
                                                                       End Sub

        ' Populate the table.
        addRow(1, #1/1/2016 11:11:11 AM#, "First row", 100D)
        addRow(2, #1/1/2017 10:10:10 AM#, "Second row", 200D)
        addRow(3, #1/1/2018 9:09:09 AM#, Nothing, 300.05D)
        addRow(4, #1/1/2019 8:08:08 AM#, "Fourth row", Nothing)

        ' Some dates in an array.
        Dim dates As DateTime() = {
            #1/1/2016 11:11:11 AM#,
            #1/1/2017 10:10:11 AM#,
            #1/1/2018 9:09:09 AM#,
            #1/1/2019 8:08:08 AM#
        }

        ' Dates array left join data table.
        Dim rows = From arrayRow In dates
                   Group Join tableRow In dt
                   On tableRow.Field(Of DateTime)("date") Equals arrayRow Into tableRows = Group
                   From tableRow In tableRows.DefaultIfEmpty(dt.NewRow)
                   Select
                        arrayRow,
                        Id = If(tableRow.Field(Of Integer?)("Id").GetValueOrDefault = 0, "", tableRow.Field(Of Integer)("Id").ToString),
                        [Date] = If(tableRow.Field(Of DateTime?)("Date").GetValueOrDefault = DateTime.MinValue, "", tableRow.Field(Of DateTime)("Date").ToString("dd/MM/yyyy HH:mm:ss")),
                        Description = If(String.IsNullOrEmpty(tableRow.Field(Of String)("Description")), "", tableRow.Field(Of String)("Description")),
                        Amount = If(tableRow.Field(Of Decimal?)("Amount") Is Nothing, "", tableRow.Field(Of Decimal)("Amount").ToString("C"))

        ' Display results.
        For Each row In rows
            Console.WriteLine("LDate:{0}  Id:{1}  RDate:{2}  Descr:{3}  Amount:{4}", row.arrayRow, row.Id, row.Date, row.Description, row.Amount)
        Next

        Console.ReadLine()
    End Sub

End Module

首先,我必须说这个实现(将DateTime分成两列日期和时间)似乎很奇怪,使事情变得更加困难和容易出错。将DateTime域(实际上描述了一个时刻)分成两个,即日期和时间,完全改变了第一个的含义。我强烈建议(如果你可以执行此更改)将其设为一列。

也就是说,我认为最好的方法是在您的select语句中将Date和Time列合并为一列,因此在您的数据表中会有一列DateTime,因此您可以轻松地执行相等检查

我编辑了这个例子,以满足您的需求。

现在,关于您的主要问题,您可能需要查看How to: Combine Data with LINQ by Using Joins (Visual Basic)

希望这有帮助!

答案 1 :(得分:0)

你可以像这样在表之间做左外连接

var rowDataLeftOuter = from rowLeft in lefttbl.AsEnumerable()
                       join rowRight in rightble.AsEnumerable() 
      on rowLeft[colToJoinOn] equals rowRight[strTempColName] into gj
                       from subRight in gj.DefaultIfEmpty()
               select lefttbl.ItemArray.Concat((subRight== null) ?
            (rightble.NewRow().ItemArray) :subRight.ItemArray).ToArray();

代码在C#中,但您可以使用转换器转换为vb.net