当前位置: 首页 > 知识库问答 >
问题:

在ON conflict子句中使用多个conflict_target

薛浩言
2023-03-14

我在表col1col2中有两列,它们都是唯一的索引(col1是唯一的,col2也是唯一的)。

我需要在插入到这个表,使用ON CONFLICT语法和更新其他列,但我不能在conflict_target子句中使用这两个列。

它的工作原理:

INSERT INTO table
...
ON CONFLICT ( col1 ) 
DO UPDATE 
SET 
-- update needed columns here

但是如何在几个列中做到这一点,就像这样:

...
ON CONFLICT ( col1, col2 )
DO UPDATE 
SET 
....

共有3个答案

阎雪峰
2023-03-14

在今天看来这是不可能的。ON CONFLICT语法的最新版本既不允许重复该子句,也不允许使用CTE:不可能从ON CONFLICT中重新插入以添加更多冲突目标。

罗兴运
2023-03-14
CREATE TABLE dupes(col1 int primary key, col2 int, col3 text,
   CONSTRAINT col2_unique UNIQUE (col2)
);

INSERT INTO dupes values(1,1,'a'),(2,2,'b');
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1) DO UPDATE SET col3 = 'c', col2 = 2

我们称之为Q1。结果是

ERROR:  duplicate key value violates unique constraint "col2_unique"
DETAIL:  Key (col2)=(2) already exists.

冲突_目标可以执行唯一索引推断。在执行推理时,它由一个或多个index_column_name列和/或index_表达式以及可选的index_谓词组成。所有表名唯一索引(不考虑顺序)都会被推断(选择)为仲裁器索引,这些索引恰好包含冲突目标指定的列/表达式。如果指定了索引_谓词,作为推理的进一步要求,它必须满足仲裁器索引。

这给人的印象是,下面的查询应该可以工作,但它不能工作,因为它实际上需要在col1和col2上同时使用唯一索引。然而,这样的索引不能保证col1和col2单独是唯一的,这是OP的要求之一。

INSERT INTO dupes values(3,2,'c') 
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2

让我们将此查询称为Q2(由于语法错误而失败)

Postgresql这样做是因为第二列发生冲突时应该发生什么没有很好地定义。有很多可能性。例如,在上面的Q1查询中,当col2上发生冲突时,postgresql应该更新col1吗?但是如果这导致col1上的另一个冲突呢?postgresql应该如何处理呢?

一个解决方案是将冲突与老式的升级相结合。

CREATE OR REPLACE FUNCTION merge_db(key1 INT, key2 INT, data TEXT) RETURNS VOID AS
$$
BEGIN
    LOOP
        -- first try to update the key
        UPDATE dupes SET col3 = data WHERE col1 = key1 and col2 = key2;
        IF found THEN
            RETURN;
        END IF;

        -- not there, so try to insert the key
        -- if someone else inserts the same key concurrently, or key2
        -- already exists in col2,
        -- we could get a unique-key failure
        BEGIN
            INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col1) DO UPDATE SET col3 = data;
            RETURN;
        EXCEPTION WHEN unique_violation THEN
            BEGIN
                INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col2) DO UPDATE SET col3 = data;
                RETURN;
            EXCEPTION WHEN unique_violation THEN
                -- Do nothing, and loop to try the UPDATE again.
            END;
        END;
    END LOOP;
END;
$$
LANGUAGE plpgsql;

您需要修改这个存储函数的逻辑,以便它能以您希望的方式完全更新列。像这样调用它

SELECT merge_db(3,2,'c');
SELECT merge_db(1,2,'d');
翟志新
2023-03-14

冲突上的需要一个唯一的索引*来进行冲突检测。所以你只需要在两列上创建一个唯一的索引:

t=# create table t (id integer, a text, b text);
CREATE TABLE
t=# create unique index idx_t_id_a on t (id, a);
CREATE INDEX
t=# insert into t values (1, 'a', 'foo');
INSERT 0 1
t=# insert into t values (1, 'a', 'bar') on conflict (id, a) do update set b = 'bar';
INSERT 0 1
t=# select * from t;
 id | a |  b  
----+---+-----
  1 | a | bar

*除了唯一索引外,还可以使用排除约束。这些约束比唯一约束更一般。假设您的表中有idvalid_-time(并且valid_-time是一个tsrange),您希望允许重复的ids,但不允许重叠的时间段。唯一的约束对您没有帮助,但使用排除约束时,您可以说“如果新记录的id等于旧的id,并且它们的有效时间与其有效时间重叠,则排除新记录”

 类似资料:
  • 我在后文10中有如下陈述: 但是,由于我插入了多个值,除了这个值之外,我还有什么选择:

  • 问题内容: 我正在尝试获取id = 3或id = 9或id = 100的内容…请记住,我可以拥有几百个这些ID。 编写查询的最有效方法是什么? 问题答案:

  • 问题内容: 这个问题已经在这里有了答案 : SQL IN子句1000项目限制 (4个答案) 6年前关闭。 我有一条SQL语句,我想在其中使用子句来获取1200的数据。当我在IN子句中包含1000个以上时,Oracle表示不允许这样做。为了克服这个问题,我尝试如下更改SQL代码: 该代码已成功执行,但结果很奇怪(在所有期间都获取了计算结果,而不仅仅是200912,这不是我想要的)。是否应该使用bet

  • 问题内容: 在WHERE子句中有使用SELECT语句描述的名称吗?这是好/不好的做法吗? 这会是更好的选择吗? 它远没有那么优雅,但是运行起来比以前的版本要快。我不喜欢它,因为它在GUI中没有非常清晰地显示(并且SQL初学者需要理解它)。我可以将其分为两个独立的查询,但是随后事情变得混乱了…… 注意:我不仅需要日期和分数(例如姓名) 问题答案: 称为相关子查询。它有它的用途。

  • 我希望获取具有特定post_id并且其'like'列设置为1的行。我怎么能再放一个条款呢?

  • 问题内容: 我正在尝试使用该子句从表中检索数据。我想从中获取除存在的行以外的所有行。据我了解,以下内容不起作用: 我可以使用的唯一方法似乎是从相同的表中选择,或者从不同的表中选择具有相同列名的列。 有人可以解释一下如何最好地使用解释子句吗? 问题答案: 您的查询似乎完全有效: 列 名 与查询无关。只有 数据类型 必须匹配。您的查询的输出列名称为,仅因为它是first中的列名称。您可以使用任何别名。