使用 psycopg2 批量插入多行
Batch upsert multiple rows with psycopg2
我需要使用 psycopg2 一次将多行更新插入 (INSERT ... ON CONFLICT DO UPDATE
) 到 postgreSQL 数据库中。本质上,我有一个表示“行”的元组列表,我需要将它们插入数据库,或者在发生冲突时更新数据库。我需要(可能)更新每一列(如果未插入)以及每一行。
我尝试了两种主要方法,使用 psycopg2 的 cursor.execute()
函数和 execute_many()
函数。首先,我做了以下事情:
upsert_statement = 'INSERT INTO table (col1, col2, col3) VALUES %s ON CONFLICT (col1) DO UPDATE SET (col1, col2, col3) = ROW (excluded.*) WHERE table IS DISTINCT FROM excluded'
psycopg2.extras.execute_values(cursor, upsert_statement, values)
我创建了一个 SQL 语句,该语句使用 execute_many()
插入值(其中传递给它的 values
是一个元组列表),并且在发生冲突时列值应该是更新为排除。但是,我收到错误 SyntaxError: number of columns does not match number of values
sometimes,即使我知道 fact 列数和值是相同的.
所以,我尝试只使用 execute()
:
upsert_statement = f'INSERT INTO table (col1, col2, col3) VALUES (value1, value2, value3), (value4, value5, value6)... ON CONFLICT (col1) DO UPDATE SET (col1, col2, col3) = (value1, value2, value3), (value4, value5, value6)...'
cursor.execute(upsert_statement)
在这里,我将批量更新插入作为 SQL 的一部分,因此不必使用 execute_values()
。但是,我在 DO UPDATE SET
之后得到一个 SyntaxError
,因为我认为 (col1, col2, col3) = (value1, value2, value3), (value4, value5, value6)...
.
是无效的
我做错了什么?如何使用 psycopg2 批量插入多行?
(我要注意,在现实中,(col1, col2, col3)
和(value1, value2, value3)
是动态的,经常变化)
您需要在 ON CONFLICT
语句中使用 table EXCLUDED
而不是值文字。这是一个特殊的 table 保存建议插入的值。您也不需要重新设置冲突的值,只需重新设置其余值即可。
INSERT INTO table (col1, col2, col3)
VALUES
(value1, value2, value3),
(value4, value5, value6)
ON CONFLICT (col1) DO UPDATE
SET (col2, col3) = (EXCLUDED.col2, EXCLUDED.col3);
为了便于阅读,如果您对 f 字符串进行三重引号,则可以格式化内联 SQLs。我不确定是否以及哪些 IDE 可以检测到它是 Python 中的内联 SQL 并切换语法突出显示,但我发现缩进很有帮助。
upsert_statement = f"""
INSERT INTO table (col1, col2, col3)
VALUES
({value1}, {value2}, {value3}),
({value4}, {value5}, {value6})
ON CONFLICT (col1) DO UPDATE
SET (col2, col3) = (EXCLUDED.col2, EXCLUDED.col3)"""
这是一个简单的测试:
drop table if exists test_70066823 cascade;
create table test_70066823 (
id integer primary key,
text_column_1 text,
text_column_2 text);
insert into test_70066823 select 1,'first','first';
insert into test_70066823 select 2,'second','second';
select * from test_70066823;
-- id | text_column_1 | text_column_2
------+---------------+---------------
-- 1 | first | first
-- 2 | second | second
--(2 rows)
insert into test_70066823
values
(1, 'third','first'),
(3, 'fourth','third'),
(4, 'fifth','fourth'),
(2, 'sixth','second')
on conflict (id) do update
set text_column_1=EXCLUDED.text_column_1,
text_column_2=EXCLUDED.text_column_2;
select * from test_70066823;
-- id | text_column_1 | text_column_2
------+---------------+---------------
-- 1 | third | first
-- 3 | fourth | third
-- 4 | fifth | fourth
-- 2 | sixth | second
--(4 rows)
您可以参考this以提高插入性能。使用简单的基于字符串的插入 execute
或 execute_many
是那里提到的最慢的前 2 种方法。
我需要使用 psycopg2 一次将多行更新插入 (INSERT ... ON CONFLICT DO UPDATE
) 到 postgreSQL 数据库中。本质上,我有一个表示“行”的元组列表,我需要将它们插入数据库,或者在发生冲突时更新数据库。我需要(可能)更新每一列(如果未插入)以及每一行。
我尝试了两种主要方法,使用 psycopg2 的 cursor.execute()
函数和 execute_many()
函数。首先,我做了以下事情:
upsert_statement = 'INSERT INTO table (col1, col2, col3) VALUES %s ON CONFLICT (col1) DO UPDATE SET (col1, col2, col3) = ROW (excluded.*) WHERE table IS DISTINCT FROM excluded'
psycopg2.extras.execute_values(cursor, upsert_statement, values)
我创建了一个 SQL 语句,该语句使用 execute_many()
插入值(其中传递给它的 values
是一个元组列表),并且在发生冲突时列值应该是更新为排除。但是,我收到错误 SyntaxError: number of columns does not match number of values
sometimes,即使我知道 fact 列数和值是相同的.
所以,我尝试只使用 execute()
:
upsert_statement = f'INSERT INTO table (col1, col2, col3) VALUES (value1, value2, value3), (value4, value5, value6)... ON CONFLICT (col1) DO UPDATE SET (col1, col2, col3) = (value1, value2, value3), (value4, value5, value6)...'
cursor.execute(upsert_statement)
在这里,我将批量更新插入作为 SQL 的一部分,因此不必使用 execute_values()
。但是,我在 DO UPDATE SET
之后得到一个 SyntaxError
,因为我认为 (col1, col2, col3) = (value1, value2, value3), (value4, value5, value6)...
.
我做错了什么?如何使用 psycopg2 批量插入多行?
(我要注意,在现实中,(col1, col2, col3)
和(value1, value2, value3)
是动态的,经常变化)
您需要在 ON CONFLICT
语句中使用 table EXCLUDED
而不是值文字。这是一个特殊的 table 保存建议插入的值。您也不需要重新设置冲突的值,只需重新设置其余值即可。
INSERT INTO table (col1, col2, col3)
VALUES
(value1, value2, value3),
(value4, value5, value6)
ON CONFLICT (col1) DO UPDATE
SET (col2, col3) = (EXCLUDED.col2, EXCLUDED.col3);
为了便于阅读,如果您对 f 字符串进行三重引号,则可以格式化内联 SQLs。我不确定是否以及哪些 IDE 可以检测到它是 Python 中的内联 SQL 并切换语法突出显示,但我发现缩进很有帮助。
upsert_statement = f"""
INSERT INTO table (col1, col2, col3)
VALUES
({value1}, {value2}, {value3}),
({value4}, {value5}, {value6})
ON CONFLICT (col1) DO UPDATE
SET (col2, col3) = (EXCLUDED.col2, EXCLUDED.col3)"""
这是一个简单的测试:
drop table if exists test_70066823 cascade;
create table test_70066823 (
id integer primary key,
text_column_1 text,
text_column_2 text);
insert into test_70066823 select 1,'first','first';
insert into test_70066823 select 2,'second','second';
select * from test_70066823;
-- id | text_column_1 | text_column_2
------+---------------+---------------
-- 1 | first | first
-- 2 | second | second
--(2 rows)
insert into test_70066823
values
(1, 'third','first'),
(3, 'fourth','third'),
(4, 'fifth','fourth'),
(2, 'sixth','second')
on conflict (id) do update
set text_column_1=EXCLUDED.text_column_1,
text_column_2=EXCLUDED.text_column_2;
select * from test_70066823;
-- id | text_column_1 | text_column_2
------+---------------+---------------
-- 1 | third | first
-- 3 | fourth | third
-- 4 | fifth | fourth
-- 2 | sixth | second
--(4 rows)
您可以参考this以提高插入性能。使用简单的基于字符串的插入 execute
或 execute_many
是那里提到的最慢的前 2 种方法。