【问题标题】:Anyway to Upsert database using PostgreSQL in Python无论如何要在 Python 中使用 PostgreSQL 更新数据库
【发布时间】:2025-12-14 08:05:02
【问题描述】:

我想以最少的努力进行更新,为简单起见,我减少了列,这不起作用:

sql = '''INSERT INTO temp.tickets
    (id, created_at, updated_at, emails, status)
VALUES
    (%s, %s, %s, %s, %s)
    ON CONFLICT (id)
    DO UPDATE SET ( emails, status) values (%s,%s) 
    
    '''

cursor = cm.cursor()
## cm is a custom module
cursor.execute(sql, (ticket['id'],
                     ticket['created_at'],
                     ticket['updated_at'],
                     ticket['emails'], ticket['status'], )

此代码显示错误:

    return super(DictCursor, self).execute(query, vars)
IndexError: tuple index out of range

我需要更改cursor.execute() 中的哪些内容才能工作?

波纹管代码有效,但我喜欢使用 %s 而不是每列输入:email = excluded.email

sql = '''INSERT INTO temp.tickets
    (id, created_at, updated_at, emails, status)
VALUES
    (%s, %s, %s, %s, %s)
    ON CONFLICT (id)
    DO UPDATE SET emails = excluded.eamils, status = excluded.status
    
    '''

cursor = cm.cursor() 
# cm is a custom module

cursor.execute(sql, (ticket['id'],
                     ticket['created_at'],
                     ticket['updated_at'],
                     ticket['emails'], ticket['status'], )

有两个相关问题link1link2

【问题讨论】:

    标签: python sql database postgresql upsert


    【解决方案1】:

    我会尝试这样的:

    sql = '''INSERT INTO temp.tickets
        (id, created_at, updated_at, emails, status)
    VALUES
        (%s, %s, %s, %s, %s)
        ON CONFLICT (id)
        DO UPDATE SET ( emails, status) values (%s,%s) 
        
        '''
    
    cursor = cm.cursor()
    ## cm is a custom module
    cursor.execute(sql, (ticket['id'],
                         ticket['created_at'],
                         ticket['updated_at'],
                         ticket['emails'], 
                         ticket['status'],
                         ticket['emails'], 
                          ticket['status'] )
    

    %s 的三个数量必须与参数的数量匹配。

    【讨论】:

    • 当我更改 cursor.execute() 时它显示:Error: psycopg2.errors.SyntaxError: syntax error at or near "values" LINE 6: DO UPDATE SET ( emails, status) values (ARRAY['su...
    【解决方案2】:

    当 Postgres 遇到捕获的冲突时,它基本上会创建一个名为 EXCLUDED 的记录,其中包含您尝试插入的值,您可以在 DO UPDATE 中引用此记录。请尝试以下操作:

    INSERT INTO temp.tickets
        (id, created_at, updated_at, emails, status)
    VALUES
        (%s, %s, %s, %s, %s)
        ON CONFLICT (id)
        DO UPDATE 
              SET emails = excluded.emails
                , status = excluded.status
                , updated_at = excluded.updated_at    -- my assumption.  
     ... 
    

    您必须按照源语言的要求进行格式化。

    【讨论】:

    • 所以这意味着我需要为每一列输入?
    • 我不确定您所说的“键入每一列”是什么意思。您已经编码了 %s ,我认为每个都是您插入的参数。这不应改变您在原始语句中传递它们的方式。如果你的意思是你必须再次分配一个数据类型,这将是你的源语言的一个特性。 唯一与原始版本不同的是 DO UPDATE 之后的代码。
    • 您的答案与我的第二个有效代码块相同,我的意思是任何其他方式不要为每个参数输入这样的 emails = excluded.emails,而是使用 %s 并更改 python 代码下面。
    • 很遗憾没有。 Postgres 完全不知道特定占位符 (%s) 的 ant 的含义。它只知道位置,如果您多次使用源中的相同变量,则每次出现都需要一个占位符。这就是@Renato 的建议。您可能可以使用数字表示法,因此电子邮件是 %4s,状态是 %5s 的状态,并在 DO 更新中重复相同的操作,但我不知道 Postgres 是否支持。我从来没有为了避免打字而尝试过。
    • 那么你有新的解决方案吗?你能打字吗? @belayer