从 pandas 数据帧执行 SQL 更新语句
Executing an SQL update statement from a pandas dataframe
上下文:我正在使用 MSSQL、pandas 和 pyodbc。
步骤:
- 使用 pyodbc 从查询中获取数据帧(没问题)
- 处理列以生成新(但已存在)列的上下文
- 用 UPDATE 语句填充辅助列(即
UPDATE t SET t.value = df.value FROM dbo.table t where t.ID = df.ID
)
现在如何执行辅助列中的 sql 代码,而不循环遍历每一行?
示例数据
前两列是通过查询得到的dbo.table
,第三列存在但在数据库中为空。第四列仅存在于数据框中,用于准备对应更新 dbo.table
的 SQL 语句
ID
raw
processed
strSQL
1
lorum.ipsum@test.com
lorum ipsum
UPDATE t SET t.processed = 'lorum ipsum' FROM dbo.table t WHERE t.ID = 1
2
rumlo.sumip@test.com
rumlo sumip
UPDATE t SET t.processed = 'rumlo sumip' FROM dbo.table t WHERE t.ID = 2
3
...
...
...
我想以高效的方式在每一行中执行 SQL 脚本。
在我在对该问题的评论中推荐 .executemany()
之后,@Charlieface 随后的评论建议 table 值参数 (TVP) 将提供更好的性能。我认为这不会有太大的不同,但我错了。
对于现有的 table 名为 MillionRows
ID TextField
-- ---------
1 foo
2 bar
3 baz
…
和表格的示例数据
num_rows = 1_000_000
rows = [(f"text{x:06}", x + 1) for x in range(num_rows)]
print(rows)
# [('text000000', 1), ('text000001', 2), ('text000002', 3), …]
我的测试使用标准 executemany()
调用 cnxn.autocommit = False
和 crsr.fast_executemany = True
crsr.executemany("UPDATE MillionRows SET TextField = ? WHERE ID = ?", rows)
用了大约 180 秒(3 分钟)。
但是,通过创建用户定义的table类型
CREATE TYPE dbo.TextField_ID AS TABLE
(
TextField nvarchar(255) NULL,
ID int NOT NULL,
PRIMARY KEY (ID)
)
和一个存储过程
CREATE PROCEDURE [dbo].[mr_update]
@tbl dbo.TextField_ID READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE MillionRows SET TextField = t.TextField
FROM MillionRows mr INNER JOIN @tbl t ON mr.ID = t.ID
END
我用的时候
crsr.execute("{CALL mr_update (?)}", (rows,))
它在大约 80 秒内完成了相同的更新(不到一半的时间)。
上下文:我正在使用 MSSQL、pandas 和 pyodbc。
步骤:
- 使用 pyodbc 从查询中获取数据帧(没问题)
- 处理列以生成新(但已存在)列的上下文
- 用 UPDATE 语句填充辅助列(即
UPDATE t SET t.value = df.value FROM dbo.table t where t.ID = df.ID
)
现在如何执行辅助列中的 sql 代码,而不循环遍历每一行?
示例数据
前两列是通过查询得到的dbo.table
,第三列存在但在数据库中为空。第四列仅存在于数据框中,用于准备对应更新 dbo.table
ID | raw | processed | strSQL |
---|---|---|---|
1 | lorum.ipsum@test.com | lorum ipsum | UPDATE t SET t.processed = 'lorum ipsum' FROM dbo.table t WHERE t.ID = 1 |
2 | rumlo.sumip@test.com | rumlo sumip | UPDATE t SET t.processed = 'rumlo sumip' FROM dbo.table t WHERE t.ID = 2 |
3 | ... | ... | ... |
我想以高效的方式在每一行中执行 SQL 脚本。
在我在对该问题的评论中推荐 .executemany()
之后,@Charlieface 随后的评论建议 table 值参数 (TVP) 将提供更好的性能。我认为这不会有太大的不同,但我错了。
对于现有的 table 名为 MillionRows
ID TextField
-- ---------
1 foo
2 bar
3 baz
…
和表格的示例数据
num_rows = 1_000_000
rows = [(f"text{x:06}", x + 1) for x in range(num_rows)]
print(rows)
# [('text000000', 1), ('text000001', 2), ('text000002', 3), …]
我的测试使用标准 executemany()
调用 cnxn.autocommit = False
和 crsr.fast_executemany = True
crsr.executemany("UPDATE MillionRows SET TextField = ? WHERE ID = ?", rows)
用了大约 180 秒(3 分钟)。
但是,通过创建用户定义的table类型
CREATE TYPE dbo.TextField_ID AS TABLE
(
TextField nvarchar(255) NULL,
ID int NOT NULL,
PRIMARY KEY (ID)
)
和一个存储过程
CREATE PROCEDURE [dbo].[mr_update]
@tbl dbo.TextField_ID READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE MillionRows SET TextField = t.TextField
FROM MillionRows mr INNER JOIN @tbl t ON mr.ID = t.ID
END
我用的时候
crsr.execute("{CALL mr_update (?)}", (rows,))
它在大约 80 秒内完成了相同的更新(不到一半的时间)。