更新冲突 postgres 上的多个列

Update multiple columns on conflict postgres

我必须编写查询来更新记录(如果存在),否则将其插入。我正在将此 update/insert 放入 postgres 数据库中。我查看了 upsert 示例,其中大多数使用最多两个字段进行更新。但是,我想更新多列。示例:

query="""INSERT INTO table (col1,col2,col3,col4,col5,col6,col7,col8,col9,..col20) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
ON CONFLICT(col2) DO UPDATE SET (col1,col2,col3,col4,col5,col6,col7,col8,col9,..col20) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""

在上面的查询中,假设 col2 是唯一键,我将插入和更新相同数量的列。我必须使用 pymysql(python 库) 执行此查询。在一个简单的插入语句中,我知道如何动态传递包含参数的元组。

cursor.execute(insert_query, data_tuple)

但在这种情况下,我有两个位置(插入和更新)输入都是动态的。考虑到上面的upsert查询,我将参数传递给游标的方式

cursor.execute(upsert_query,data_tuple,data_tuple)

但是,这个函数会抛出一个错误,执行函数中的参数数量过多。那我怎么过?此外,我正在尝试使用这种方式来传递参数,因为使用赋值(=)对于 20 列来说是一件费力的事情。

还有其他替代方法吗?就像 mysql.

中的简单 "replace into" 语句

编辑 2

所以,经过几次交流:你的问题似乎是如何在pymysql中使用cursor.execute函数。 这是相应文档的 link:https://pymysql.readthedocs.io/en/latest/modules/cursors.html

我从不在 python 中编写代码,但文档似乎对执行方法的用法非常准确:

execute(query, args=None)

Execute a query
Parameters: 

    query (str) – Query to execute.
    args (tuple, list or dict) – parameters used with query. (optional)

Returns:    

Number of affected rows
Return type:    

int

If args is a list or tuple, %s can be used as a placeholder in the query. If args is a dict, %(name)s can be used as a placeholder in the query.

所以也许 'dict' 类型是可能的,但我不认为这是它的哲学。

原版post

我不太确定 'both places input to be dynamic' 你到底想说什么,所以我会在这里写一些 SQL 如果你有任何问题,请不要犹豫:)

首先进行一个小的初始化

CREATE TABLE test 
( 
    id int,
    value_1 varchar,
    value_2 bit
);

ALTER TABLE test
ADD CONSTRAINT ck_test UNIQUE(id, value_1, value_2);


INSERT INTO test
VALUES
    (1, 'test', cast(1 as bit))
    , (2, 'test_2', cast(0 as bit));

第二个错误

INSERT INTO test
VALUES
    (1, 'test', cast(1 as bit));

第三个UPSERT

INSERT INTO test
VALUES 
    (1, 'test', cast(1 as bit))
ON CONFLICT ON CONSTRAINT ck_test
DO
    UPDATE 
        SETid = 3, value_1 = 'error';

这是在回答你的问题吗?或者它更像是一个字符串构建问题?

编辑 所以,我不喜欢其他语言,所以我会把它放在 plpgsql 中:

do language plpgsql $$
declare 
    query varchar;
    id_insert int;
    value_1_insert varchar;
    value_2_insert bit;
    id_update int;
    value_1_update varchar;
    value_2_update bit;
begin
    id_insert := 4;
    value_1_insert := 'test';
    value_2_insert := cast(1 as bit);

    id_update := id_insert;
    value_1_update := 'error';
    value_2_update := cast(0 as bit);

    query := 'INSERT INTO test
                VALUES 
                    (
                        cast('||id_insert||' as int)
                        , '''||value_1_insert||'''
                        , cast('||value_2_insert||' as bit)
                    )
                ON CONFLICT ON CONSTRAINT ck_test
                DO
                    UPDATE 
                        SET 
                            id = cast('||id_update||' as int)
                            , value_1 = '''||value_1_update||'''
                            , value_2 =  cast('||value_2_update||' as bit);';
    execute query;
end;
$$;

希望对您有所帮助 ;)

您问题的直接答案是,您执行 tuple + tuple 来加倍元组。

cursor.execute(upsert_query, data_tuple + data_tuple)

其他选项:

如果你有单独的值并且你正在构造元组,你可以直接构造一个具有两倍数量的值的元组。

query="""INSERT INTO table (col1,col2,col3,col4,col5,col6,col7,col8,col9,..col20) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
ON CONFLICT(col2) DO UPDATE SET col1=%s, col3=%s, col4=%s, ..."""

cur.execute(query, (c1, c2, c3, ... c20, c1, c3, c4, ... c20))

您必须指定两次值(col2 除外)。

如果您已经有了您最初要求的元组,那么您将使用 + 将同一个元组合并两次。

如果你有单独的值而不是元组,你也可以使用像字典这样的命名参数。

query="""INSERT INTO table (col1,col2,col3,col4...) VALUES(%(c1)s, %(c2)s, %(c3)s, %(c4)s...) ON CONFLICT(col2) DO UPDATE SET col1=%(c1)s, col3=%(c3)s, col4=%(c4)s, ..."""
cur.execute(query, {'c1': c1val, 'c2': c2val, 'c3': c3val, 'c4': c4val, ...})

这种形式可读性好,只传一次参数,以后列数变化时也便于维护(增加列等)。