我在表col1
,col2
中有两列,它们都是唯一的索引(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
....
在今天看来这是不可能的。ON CONFLICT
语法的最新版本既不允许重复该子句,也不允许使用CTE:不可能从ON CONFLICT中重新插入以添加更多冲突目标。
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');
冲突上的需要一个唯一的索引*来进行冲突检测。所以你只需要在两列上创建一个唯一的索引:
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
*除了唯一索引外,还可以使用排除约束。这些约束比唯一约束更一般。假设您的表中有id
和valid_-time
(并且valid_-time
是一个tsrange
),您希望允许重复的id
s,但不允许重叠的时间段。唯一的约束对您没有帮助,但使用排除约束时,您可以说“如果新记录的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中的列名称。您可以使用任何别名。