【问题标题】:Most performant way of comparing a huge array in vb with records in sql server?将 vb 中的巨大数组与 sql server 中的记录进行比较的最高效方法?
【发布时间】:2018-02-21 08:48:57
【问题描述】:

我在 vb.net 中有一个二维数组(出于测试目的,它只有 1000 行,但实际最多 2.5m)。

数组可以分成三组,分别依赖于SQL server中的两个字段,TD和UID:

  • TDnomatch
  • TDmatchUIDmatch
  • TDmatchUIDnomatch

逻辑:

我的原始代码只是循环遍历数组的每一行

显示结构的伪代码

for row_id  = 0 to upper_bound      
 "select ... where TD = " & Pricetable(0, rowid)
  executereader
  if sqlreader.Hasrows()
      ... (getting UID from reader and other validation)      
      closereader
      If UID <> UID_VBarray
           "delete from ... "
           executenonquery
           InsertPrices(command, UID, rowid)
      end if
  else
      closereader
      InsertPrices(command, UID, rowid)
  end if
next row_id

这非常慢,1200 条记录大约需要 3 分钟。我认为这是因为 sql server 和 vb.net 之间的所有开销通信。

所以我想尽量减少查询量,但问题是我应该在 SQL Server 还是在 vb.net 中执行逻辑

将每条记录(TD、UID)从 sql server 读入 vb.net 中的另一个数组似乎有点过头了(更不用说我可能需要更多的 ram 哈哈),但是逻辑很简单,我可以做到一些插入。

我可以将整个数组发送到 SQL Server 吗?是否建议发送带有 where ... IN / not in 的查询(250 万个元素的列表)(显然使用循环连接构建字符串)

【问题讨论】:

  • 将数据放入数据库,添加适当的索引并编写一个查询,返回您想要的结果。
  • "select ... where TD = " &amp; Pricetable(0, rowid) - 没有。如果您仍然希望在客户端使用它,请一次或部分地从数据库中读取它。您可能已经找到了执行此操作的最慢方法。
  • @Panagiotis Kanavos - 不要什么?被删除的行必须被删除,因为它们不再是真实的。 (因分拆和分红而变动前的股价)
  • 我正在回复标题 - 不要将数据拉到客户端,这是可能的最慢方式。您可以通过 LEFT JOIN 找到差异。您可以在 INSERT 或 DELETE 语句的 FROM 子句中使用 LEFT JOIN,这意味着您可以轻松编写语句来删除丢失的记录或添加新记录。如果 JOIN 中使用的列是索引的一部分(通常是主键),则性能会非常
  • 由于您必须在 SQL Server 上添加和更新行,那么最好的办法是使用 SqlBulkCopy() 将包含 250 万行的数组注入到临时表中。然后使用 SQL 语句(如 MERGE)来比较和更新 SQLServer 中的永久表。

标签: arrays sql-server vb.net performance logic


【解决方案1】:

与以前的 cmets 一样 - SQL 在此任务中表现最好,但是,我确实想提一下我在处理大量数据时所拥有的一些技巧。

字典 - 好好看看这些,因为它们在处理大数据时比列表快得多,在我的某些情况下比 SQL 快。根据您需要的结果,我可以将 750k 电子邮件记录加载到字典中

您在 RAM 上是正确的,将所有数据加载到客户端计算机的成本非常高,更不用说很容易损坏,尤其是在后台进行记录集更新时。

如果您确实想查询 SQL,请记住 con.open ...&gt;&gt;stuff&gt;&gt; ... con.close 需要很长时间来处理,因此请尽量避免查询每条记录。始终一次获取所有需要的数据,然后(如果需要)在之后进行计算。

我知道这不是一个答案,只是希望它能给你一些好的建议和想法:)

hth 鸡

【讨论】:

    【解决方案2】:

    我最终将数组转换为数据表并使用 sqlbulkcopy。然后我只使用 sql 命令,让逻辑位于我的 sql 中。注意:我使用的是动态 sql,因为 sql 注入不是威胁,这是针对用户自己的本地实例的。

     For rowid = 0 To upper_bound
            If PriceTable(0, rowid) <> Nothing Then
                workrow = DTPriceTable.NewRow()
                For columnid = 0 To 14
                    workrow(columnid) = PriceTable(columnid, rowid)
                Next
                DTPriceTable.Rows.Add(workrow)
                i += 1
                If i > 10000 Then
                    i = 0
                    SQLServerInfo.SQLUpdateInfo("Preparing records to be written", 0, 7, rowid / upper_bound)
                End If
            End If
        Next
    
        SQLServerInfo.SQLUpdateInfo("Write Prices to empty table", 1, 7)
    
        Using bc As SqlBulkCopy = New SqlBulkCopy(myConn)
            bc.DestinationTableName = "dbo.NewPrices"
            Try
                bc.WriteToServer(DTPriceTable)
                Console.WriteLine("Wrote to db")
            Catch ex As Exception
                MsgBox("Failed to write to Db")
                MsgBox(ex.Message)
            End Try
        End Using
    
        SQLServerInfo.SQLUpdateInfo("Transfering splits and dividends to SavedInfo Table", 2, 7)
        command.CommandText =
            "Insert into SavedInfo
            Select ticker, [date], ex_dividend, split_ratio, tickerdate
            From [dbo].[NewPrices]
            Where ex_dividend <> 0 Or split_ratio <> 1"
        Dim DailySplitsandDiv As Integer = command.ExecuteNonQuery()
    
        'Update the UID column
        command.CommandText = "
            update NewPrices
            Set UID_All_Concat = CONCAT([tickerdate]
              , [open]
              , [high]
              , [low]
              , [close]
              , [volume]
              , [ex_dividend]
              , [split_ratio]
              , [adj_open]
              , [adj_high]
              , [adj_low]
              , [adj_close]
              , [adj_volume])"
        command.ExecuteNonQuery()
    
        'New tickerdates
        SQLServerInfo.SQLUpdateInfo("Inserting new prices", 3, 7)
        command.CommandText =
            "Insert into Prices
            Select
                np.[ticker]
              , np.[date]
              , np.[open]
              , np.[high]
              , np.[low]
              , np.[close]
              , np.[volume]
              , np.[ex_dividend]
              , np.[split_ratio]
              , np.[adj_open]
              , np.[adj_high]
              , np.[adj_low]
              , np.[adj_close]
              , np.[adj_volume]
              , np.UID_All_Concat
              , np.tickerdate
            From [dbo].[NewPrices] as np
            Left Join Prices on np.tickerdate = Prices.tickerdate
            where Prices.tickerdate Is null"
    
        Dim RowsInserted1 As Integer = command.ExecuteNonQuery()
    
    
        'TD match UID no match (Historical data changed due to splits or dividends)
        'First delete the records that need to be updated
        SQLServerInfo.SQLUpdateInfo("Updating historical prices for splits and dividends", 4, 7)
        command.CommandText =
            "Delete Prices
            From [dbo].[Prices]
            Left Join NewPrices np on np.tickerdate = Prices.tickerdate         
            where np.UID_All_Concat <> Prices.UID_All_Concat"
    
        Dim rowsDeletedduetoSandD As Integer = command.ExecuteNonQuery()
    
        SQLServerInfo.SQLUpdateInfo("Updating historical prices for splits and dividends", 5, 7)
        command.CommandText =
            "Insert into Prices
            Select
                np.[ticker]
                , np.[date]
                , np.[open]
                , np.[high]
                , np.[low]
                , np.[close]
                , np.[volume]
                , np.[ex_dividend]
                , np.[split_ratio]
                , np.[adj_open]
                , np.[adj_high]
                , np.[adj_low]
                , np.[adj_close]
                , np.[adj_volume]
                , np.UID_All_Concat
                , np.tickerdate
    
            From [dbo].[NewPrices] as np
            Left Join Prices on np.tickerdate = Prices.tickerdate           
            where Prices.tickerdate is null"
    
        Dim RowsInserted2 As Integer = command.ExecuteNonQuery()
    
    
        'Cleanup NewPrices table
        SQLServerInfo.SQLUpdateInfo("Cleanup newprices table", 6, 7)
        command.CommandText = "Delete from dbo.NewPrices"
        command.ExecuteNonQuery()
    
        SQLServerInfo.SQLUpdateInfo("Finished", 7, 7, 100)
    End Sub
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-28
      • 2017-01-13
      • 1970-01-01
      • 1970-01-01
      • 2012-02-24
      • 2011-02-08
      • 1970-01-01
      相关资源
      最近更新 更多