使用 psycopg2 批量更新插入多行

问题描述 投票:0回答:1

我需要使用 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)

我创建了一个使用

execute_many()
插入值的 SQL 语句(其中传递给它的
values
是元组列表),并且在发生冲突时,应将列值更新为排除。但是,我收到错误
SyntaxError: number of columns does not match number of values
有时,即使我知道事实列数和值是相同的。

所以,我尝试只使用

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()
。但是,我在
SyntaxError
之后得到了
DO UPDATE SET
,因为我认为拥有
(col1, col2, col3) = (value1, value2, value3), (value4, value5, value6)...
是无效的。

我做错了什么?如何使用 psycopg2 批量更新插入多行?

(我要注意的是,实际上,

(col1, col2, col3)
(value1, value2, value3)
是动态的,并且经常变化)

python postgresql psycopg2
1个回答
3
投票

您需要在 EXCLUDED 语句中使用

table
ON CONFLICT
而不是值文字。这是一个特殊的表,保存建议插入的值。您也不需要重新设置冲突的值,只需重新设置其余的值。

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 字符串使用三引号,则可以格式化内联 SQL。我不确定是否以及哪些 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 首先 首先
2 第二 第二
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 第三 首先
3 第四 第三
4 第五 第四
2 第六 第二

您可以参考this以提高插入性能。使用简单的基于字符串的

execute
execute_many
进行插入是其中提到的最慢的 2 个方法。

© www.soinside.com 2019 - 2024. All rights reserved.