【问题标题】:Mark non-unique rows in a DataTable标记 DataTable 中的非唯一行
【发布时间】:2011-05-20 21:22:51
【问题描述】:

我有一个 DataTable,我想检查其中三列中的值是否唯一。如果没有,最后一列应该填入组合值第一次出现的行号。

比如这张表:

ID    Name    LastName    Age    Flag
-------------------------------------
1     Bart    Simpson     10      -
2     Lisa    Simpson      8      -
3     Bart    Simpson     10      -
4     Ned     Flanders    40      -
5     Bart    Simpson     10      -

应该导致这个结果:

Line  Name    LastName    Age    Flag
-------------------------------------
1     Bart    Simpson     10      -
2     Lisa    Simpson      8      -
3     Bart    Simpson     10      1
4     Ned     Flanders    40      -
5     Bart    Simpson     10      1

我通过使用两个嵌套的 for 循环迭代 DataTable 并比较值来解决这个问题。虽然这适用于少量数据,但当 DataTable 包含大量行时,会变得很慢

我的问题是:关于数据量可能在 100 到 20000 行之间变化,这个问题的最佳/最快解决方案是什么?
有没有办法使用 LINQ 做到这一点? (我不太熟悉,但我想学!)

【问题讨论】:

    标签: c# .net vb.net performance datatable


    【解决方案1】:

    我无法评论您如何在 C#/VB 中使用数据表执行此操作,但如果您可以将其全部移至 SQL,您的查询将如下所示:

    declare @t table (ID int, Name varchar(10), LastName varchar(10), Age int)
    insert into @t values (1,     'Bart' ,   'Simpson',     10 )
    insert into @t values (2,     'Lisa',    'Simpson' ,     8 )
    insert into @t values (3,     'Bart',    'Simpson' ,    10 )
    insert into @t values (4,     'Ned',     'Flanders' ,   40 )
    insert into @t values (5 ,    'Bart',    'Simpson'   ,  10 )
    
    select t.*,
    (select min(ID) as ID
        from @t t2
        where t2.Name = t.Name
        and t2.LastName = t.LastName
        and t2.id < t.id)
    from @t t
    

    我在这里定义了一个用于演示目的的表格。我想你也许可以把它翻译成 LINQ。

    【讨论】:

    • 谢谢,子选择 (+1) 是个好主意。但是,我更喜欢不需要 SQL 的解决方案。我会尝试在 linq 中做这样的事情。
    【解决方案2】:

    好的,我想我自己得到了答案。根据 James Wiseman 的回答中的建议,我尝试了 LINQ。

    Dim myErrnrFnct = Function( current, first) If(first <> current, first, 0)
    Dim myQuery = From row As DataRow In myDt.AsEnumerable _
                          Select New With { _
                            .LINE = row.Item("LINE"), _
                            .NAME = row.Item("NAME"), _
                            .LASTNAME = row.Item("LASTNAME"), _
                            .AGE = row.Item("AGE"), _
                            .FLAG = myErrnrFnct(row.Item("LINE"), myDt.AsEnumerable.First(Function(rowToCheck) _
                                                                                            rowToCheck.Item("NAME") = row.Item("NAME") AndAlso _
                                                                                            rowToCheck.Item("LASTNAME") = row.Item("LASTNAME") AndAlso _
                                                                                            rowToCheck.Item("AGE") = row.Item("AGE")).Item("LINE")) _
                          }
    

    通过这个查询,我得到了问题中描述的结果。 myErrnrFnct 函数是必需的,因为如果没有具有相同值的其他行,我希望 Flag 列具有值 0

    要再次从myQuery 中获取数据表,我必须添加此处描述的一些扩展:
    How to: Implement CopyToDataTable Where the Generic Type T Is Not a DataRow
    然后,这一行就可以了:

    Dim myNewDt As DataTable = myQuery.CopyToDataTable()
    

    这似乎工作得很好。有什么建议可以做得更好吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多