等同于ON CONFLICT DO NOTHING FOR UPDATE postgres

时间:2019-01-11 15:15:56

标签: sql postgresql sql-update

如果更新的版本不违反主键约束,我想更新我的postgres数据库中的行。如果可以,我想保留该行。

假设该表在col1, col2, col3上具有主键,如果我运行这样的查询:

UPDATE table SET (col1, col2) = ('AAA', 'BBB') 
      WHERE col1='AAB' AND col2='BBA';

查询将失败,如果存在两个条目,我将得到重复的键错误:

'AAA', 'BBB', 'CCC'
'AAB', 'BBA', 'CCC'

col3在现有行和要更新的行之间是相同的。

如果我要INSERT行,我会使用ON CONFLICT DO NOTHING,但找不到UPDATE的实现。是否存在等价物?

2 个答案:

答案 0 :(得分:3)

AFAIK,没有这样的等效项。

让我们说您正在开发一个连接到postgresql数据库的应用程序,在您遇到问题时,需要牢记以下几点:

  • 这可能是违反直觉的,但是您应该将数据库抛出的错误视为
    这只是要获得状态,并不意味着应用程序崩溃。
  • 对于插入,还有另一种选择的操作on conflict(更新或不执行任何操作),因此有意义的是让您决定的语法。
    对于更新,您唯一可以做的就是……什么都没有。
    那么,由于没有选择,SQL为什么会让您要求做特定的事情?请记住,数据库报告错误,因此,让数据库什么都不做,告诉您原因。
  • 最后,更新主键是一个坏习惯。
    插入的ON CONFLICT ...无意更新主键字段。事实恰恰相反:打算在单个记录中更新所有除主键中的字段以外的字段。

尽管我在这一点上,但请注意,主键无需冲突即可使查询失败
1条带有“便捷” ON UPDATE NO ACTION外键的记录也会使其失败(这比用ON UPDATE CASCADE更新50个表中的10M +条记录更好)。顺便说一句,您知道Oracle甚至没有ON UPDATE CASCADE子句吗?您认为是什么原因?


在这种情况下您不能做什么?

  1. 请勿更新主键,就像我说的那样。您的问题对于UNIQUE约束仍然有效,但是请请不要更新主键。
  2. 请勿尝试查看是否存在冲突的记录。这可能需要很长时间,但仍然不可靠。
    您真的要选择数百万条记录只是为了避免错误代码吗?
    另外,当您扩展到其他约束条件(CHECKEXCLUSION)时,您是否会真正键入它所需要的附加代码而没有错误,以便再次避免出现错误代码?
    最后,如果您已实现行级安全性,则冲突可能是由您看不到的记录引起的。
  3. 处理您应用中的错误代码。接收状态为良好
  4. 如果您正在交易中,请使用保存点
    这是唯一出现DB错误的烦人的事情:如果在事务处理过程中遇到一个错误,那么您将开始为所有事务获取current transaction is aborted, commands ignored until end of transaction block
    希望您不需要回滚整个事务并从头开始重做所有操作。您可以使用下面的代码来摆脱困境。

您在这里:

BEGIN;
SAVEPOINT MySavepoint;
UPDATE mytable set myuniquefield = 3; /*2+ records are going to be updated */
rollback to savepoint MySavepoint;
/*Insert Some more queries here*/
COMMIT;

答案 1 :(得分:2)

您可以将关联子查询与WHERE NOT EXISTS子句一起使用,以确保您的更新不会产生重复项,例如:

UPDATE mytable t
SET (col1, col2) = ('AAA', 'BBB')
WHERE t.col1 = 'AAB' and t.col2 = 'BBA'
AND NOT EXISTS (
   SELECT 1 FROM mytable WHERE col1 = 'AAA' AND col2 = 'BBB' AND col3 = t.col3
);

this db fiddle中进行了测试。

编辑

正如罗曼·科诺瓦尔(Roman Konoval)所评论的那样,请注意,如果UPDATE运行时并发事务插入相同的密钥,这仍将产生重复的密钥错误。指出更新表的主键不是一个好习惯(有关此问题的详细讨论,请参见下面来自@Lau的答案)。

相关问题