更新冲突postgres上的多个列

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

我必须写一个查询来更新记录,如果它存在,否则插入它。我正在进行更新/插入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)

但是,这个引发了一个错误,其中参数的数量在execute函数中。那我怎么通过?此外,我试图使用这种方式传递参数,因为使用赋值(=)对于20列来说是一件费力的事情。

有没有其他替代方法来做到这一点?就像在mysql中简单的“替换成”语句一样。

postgresql sql-insert pymysql upsert on-duplicate-key
2个回答
2
投票

你的问题的直接答案是,你做一个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, ...})

此表单有利于可读性,仅传递参数一次,并且如果列数在将来发生变化,则易于维护(增加列等)。


1
投票

编辑2

所以,经过几次交流:你的问题似乎是如何在pymysql中使用cursor.execute函数。这是相应文档的链接: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'类型是可能的,但我不认为这是它的哲学。

原帖

我不太确定你想说的“两个地方输入都是动态的”,所以我会在这里放下一些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;
$$;

希望这可以帮助 ;)

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