【问题标题】:Efficient way to get unique records from datatable从数据表中获取唯一记录的有效方法
【发布时间】:2011-10-26 10:30:23
【问题描述】:

我有一个包含多个列的 DataTable,包括 AccountNumber、Year 和 Month。我正在将此表中的信息导出到 Excel 工作簿。由于这张表有可能非常大,所以我必须检查表中的记录数(因为Excel只能有65536行或类似的东西。如果原始表足够小,我需要将所有记录放入在单个工作表中。如果一个工作表的记录太多,我需要根据 AccountNumber 将记录分成多个工作表。此外,如果特定 AccountNumber 的记录太多,那么我需要将该 AccountNumber 拆分为多个工作表基于年份。如果特定年份仍然有太多记录,那么我必须按月份拆分它们。

例如:

如果我总共有 500,000 条记录,我必须通过 AccountNumber 拆分它们:

工作表名称----记录数

  • 1111 ---- 150,000
  • 2222 ---- 50,000
  • 3333 ---- 100,000

然后我需要根据年份将帐户 1111 和 3333 拆分为多个工作表。然后我会有这样的东西:

工作表名称----记录数

  • 1111 - 2010 ---- 50,000
  • 1111 - 2011 ---- 100,000
  • 2222 ---- 50,000
  • 3333-2010 ---- 50,000
  • 3333-2011 ---- 50,000

然后,由于 1111 - 2011 仍然太大,我不得不根据月份拆分那个,最后给出:

工作表名称----记录数

  • 1111-2010 ---- 50,000
  • 1111-201105 ---- 30,000
  • 1111-201106 ---- 30,000
  • 1111-201107 ---- 40,000
  • 2222 ---- 50,000
  • 3333-2010 ---- 50,000
  • 3333-2011 ---- 50,000

用于创建 Excel 文件的代码是我公司编写的一个项目。为简单起见,该函数接受一个 DataTable 并以 Excel 电子表格的格式写出 DataTable 中的记录。关于如何在不调用数据库的情况下做到这一点的任何想法?提前致谢。

【问题讨论】:

  • 仅供参考,Excel 2007 及更高版本可以有 100 万行。
  • 是的,但是我们公司的一些电脑还在使用2003
  • 似乎升级它们可能更容易;)
  • 我完全同意,但遗憾的是这不取决于我:(

标签: .net vb.net visual-studio-2010 datatable dataview


【解决方案1】:

您可以使用LINQ-to-DataSetDataRows 进行相应的分组,并将它们添加到单独的DataTables 中,行数少于65536。

看看这个工作示例,它也生成了正确的文件/数据表名称:

Private Shared sampleAccountNumbers As List(Of Int32) = {1111, 2222, 3333, 4444}.ToList
Private Const MAX_ROWS = 65536
Private tblSource As New DataTable()
Private allResultTables As New List(Of DataTable)

Private Sub SplitTableRows()
    Dim accQuery = _
        From row In tblSource
        Group row By AccountNumber = row("AccountNumber") Into AccNumGroup = Group
    For Each acc In accQuery
        If acc.AccNumGroup.Count > MAX_ROWS Then
            Dim yearQuery = _
               From row In acc.AccNumGroup
               Group row By Year = row("Year") Into YearGroup = Group
            For Each y In yearQuery
                If y.YearGroup.Count > MAX_ROWS Then
                    Dim monthQuery = _
                       From row In y.YearGroup
                       Group row By Month = row("Month") Into MonthGroup = Group
                    For Each m In monthQuery
                        If m.MonthGroup.Count > MAX_ROWS Then
                            'split by days or whatever...'
                        Else
                            Dim tblMonth = m.MonthGroup.CopyToDataTable()
                            tblMonth.TableName = String.Format("{0}-{1}{2}", acc.AccountNumber, y.Year, m.Month)
                            allResultTables.Add(tblMonth)
                        End If
                    Next
                Else
                    Dim tblYear = y.YearGroup.CopyToDataTable()
                    tblYear.TableName = String.Format("{0}-{1}", acc.AccountNumber, y.Year)
                    allResultTables.Add(tblYear)
                End If
            Next
        Else
            Dim tblAcc = acc.AccNumGroup.CopyToDataTable()
            tblAcc.TableName = acc.AccountNumber
            allResultTables.Add(tblAcc)
        End If
    Next
End Sub

Private Sub BtnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnStart.Click
    InitSourceDataTable()
    If tblSource.Rows.Count > MAX_ROWS Then
        SplitTableRows()
    Else
        allResultTables.Add(tblSource)
    End If
    For Each tbl In allResultTables
        'call your generate-excel-method...'
    Next
End Sub

Private Sub InitSourceDataTable()
    Dim rndAccNum As New Random(Date.Now.Millisecond)
    Dim rndYear As New Random(Date.Now.Millisecond)
    Dim rndMonth As New Random(Date.Now.Millisecond)
    tblSource.Columns.Add(New DataColumn("ID", GetType(Integer)))
    tblSource.Columns.Add(New DataColumn("AccountNumber", GetType(Integer)))
    tblSource.Columns.Add(New DataColumn("Year", GetType(Integer)))
    tblSource.Columns.Add(New DataColumn("Month", GetType(Integer)))
    For i As Int32 = 1 To 500000
        Dim newRow = tblSource.NewRow
        newRow("ID") = i
        newRow("AccountNumber") = sampleAccountNumbers(rndAccNum.Next(0, sampleAccountNumbers.Count))
        newRow("Year") = rndAccNum.Next(2005, 2012)
        newRow("Month") = rndMonth.Next(1, 13)
        tblSource.Rows.Add(newRow)
    Next
End Sub

此示例在 2005-2012 年期间随机使用 4 个静态 AccountNumbers,其中包含 500.000 条记录。由于 LINQ-to-DataSet 完全在内存中工作,因此它仅在 1.6 秒内生成 28 个 DataTable 并克隆所有 DataRows。

【讨论】:

  • 非常感谢。我不得不改变一些小细节,但这是一个很大的帮助。
【解决方案2】:

您是否专门为此操作从数据库中提取数据?如果是这样,请使用 SELECT DISTINCT 并在相关列上放置数据库索引。这样,您就不必担心——DBMS 会为您过滤掉重复的行。您剩下要做的就是遍历行并根据需要对它们进行分组。

如果您在对行进行分组时遇到困难,可以将 COUNT() 添加到 select 和 group by 子句中,DBMS 会告诉您有多少行适合该类别:

SELECT *, COUNT(*)AS row_count GROUP BY accountid, year, month, etc...;

您的行将如下所示:

c1   c2   c3   row_count
---- ---- ---- ---------
xxx, xxx, xxx, 1
yyy, yyy, yyy, 2
yyy, yyy, zzz, 2

【讨论】:

  • 不,Excel 生成并不是这个 DataTable 唯一的用途。
  • Excel 不是数据库。 Excel是正确的选择吗?考虑使用 Access 或 csv 文件。
  • 它正在生成一个 Excel 报告供我们的销售人员使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多