【问题标题】:How to save 15,000 records quickly in SQL Server?如何在 SQL Server 中快速保存 15,000 条记录?
【发布时间】:2021-09-10 20:07:07
【问题描述】:

我在 SQL Server 中有如下表:

我有一个 API 每 10 秒接收 15,000 条记录以保存在上表中。好像userid 如果不存在用户标识插入记录,则更新该行。我使用以下代码用 pyodbc (在 python 中)编写每个用户的记录,这意味着我运行以下代码 15,000 次:

update Mytable 
set buy = ?, model = ?, price = ?, color = ?, number = ?,
    balance = ?, time = ?, type = ?, 
where userid = ?

if @@ROWCOUNT = 0
    insert into Mytable (userid, buy, model, price, color, 
                         number, balance, time, type)
    values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

上面的代码运行良好,但 15,000 条记录需要 700 秒。我只有这个 API,我没有关于用户数量的其他信息......

如何在 7 秒内保存 15,000 条记录?

【问题讨论】:

  • @GordThompson 我的问题是速度慢,不存储数据
  • 看起来您一次处理一行,这肯定会很慢。批量上传行然后在服务器上合并它们会更快。
  • 是的,批量处理。您需要一个将消息作为一组释放到 SQL 的缓冲区。您可能需要设置两个释放条件 - 缓冲区已满或计时器已过,以先到者为准。

标签: sql-server sql-update sql-insert pyodbc


【解决方案1】:

如何在 7 秒内保存 15,000 条记录?

最重要的事情(按重要性排序)是:

  1. 不向 SQL Server 发送 15,000 个单独的批处理或 RPC

  2. 不运行 15,000 个单独的事务

  1. 不运行 15,000 个单独的 DML 语句。

从 Python 执行此操作的最简单方法是将数据作为 JSON 文档发送,然后使用批量更新/插入或合并来插入/更新数据。

您可以发送全部 15,000 个或分成几批发送。参见例如Trying to insert pandas dataframe to temporary table

在服务器上拥有数据后,您可以使用 TSQL 的 MERGE,或者对整个批处理使用单个 UPDATE 和单个 INSERT 语句。

【讨论】:

  • 谢谢,如果存在的话,你能给我插入和同时更新的 TSQL 代码吗?
  • 检查 MERGE 的链接。
  • 第 1 步将数据作为 JSON 插入然后删除重复的旧记录是否有意义?
  • TIL:我刚刚使用 fast_executemany=True 对普通的旧 df.to_sql() 进行了 JSON 批量上传测试。我预计 JSON 方法会慢一些,因为 JSON 会为每一行重复列名并将所有值表示为字符串会产生额外开销,但事实证明,.to_sql() 上传 5 列 x 的时间实际上增加了近 40% 1_000_000 行。我猜 pyodbc 构建参数数组所花费的时间比通过网络推送打包数据所节省的时间要长得多。 (当然是 YMMV。)
  • 好的,所以 JSON 上传确实产生了更多的网络流量:234 MiB 与 .to_sql() 的 164 MiB。尽管如此,JSON 方法始终更快,Go 数字。
【解决方案2】:

感谢 @David Browne 和 This tutorial 在 SQL Server 中使用 OPENJSON。 我使用OPENJSON 并在 1 秒内使用以下代码(在 pyodbc 中)写入 15000 行,然后使用 1 个查询删除重复的旧记录。

写入 1500 行:

import pyodbc 
conn = pyodbc.connect('Driver={SQL Server};'
                      'Server=server_name;'
                      'Database=database_name;'
                      'Trusted_Connection=yes;')

cursor = conn.cursor()
 SqlSave=   DECLARE @json NVARCHAR(max) = ' [ 
          
                { "userid" : 14F2G34, "buyV":"pen" ,"modelM" : "Bic", "color" : "red","numberB" : 4000,"balanceVal" : 750,"timeBuy" : 1631303488,"type":"simple" },
                { "userid" : 14F2G35, "buyV":"pen" ,"modelM" : "blueBic", "color" : "blue","numberB" : 1000,"balanceVal" : 150,"timeBuy" : 1631303488,"type":"coly" },
                { "userid" : 14F2G36, "buyV":"pen" ,"modelM" : "oli", "color" : "yellow","numberB" : 6000,"balanceVal" : 200,"timeBuy" : 1631303488,"type":"ni" },
            
      ...
    
    
        ]';
    
    INSERT INTO Mytable 
    SELECT * 
    FROM OPENJSON(@json, '$')
    WITH  (
            useruserid      int   '$.userid',  
            buy   varchar(60)     '$.buyV', 
            model   varchar(60)     '$.modelM', 
            color   varchar(60)     '$.color', 
            number   varchar(60)     '$.numberB', 
            balance   varchar(60)     '$.balanceVal', 
            time   varchar(60)     '$.timeBuy', 
            type   varchar(60)     '$.type', 
            
            
         
        );
cursor.execute(SqlSave)
cursor.commit()

注意:如果上面的字符串长度超过 4000,请使用 NVARCHAR(max)

对于更新存在的行,我删除了旧行,我有自动增量的 ID 列,我删除了 userid 列中的所有重复行,除了重复行,其具有以下代码的最大 ID:

delete FROM mytable
WHERE ID NOT IN
(   SELECT max(ID)
FROM mytable
GROUP BY userid
)

【讨论】:

    猜你喜欢
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    • 2012-09-29
    • 2013-06-05
    • 1970-01-01
    相关资源
    最近更新 更多