从数据表获取唯一记录的有效方法

时间:2011-08-13 14:41:10

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

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

例如:

如果我总共有500,000条记录,我必须按AccountNumber分割它们:

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

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

然后我需要将Account 1111和3333分成多个基于Year的工作表。我会有这样的事情:

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

  • 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中的记录。关于如何在不再调用数据库的情况下执行此操作的任何想法?提前谢谢。

2 个答案:

答案 0 :(得分:0)

您是否专门为此操作从数据库中提取数据?如果是这样,请使用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

答案 1 :(得分:0)

您可以使用LINQ-to-DataSet相应地对DataRows进行分组,并将其添加到单独的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。