UPSERT with on 冲突两个或多个约束如何?

How do UPSERT with on conflict two or more constraints?

我有一个 table:

我想插入或更新从另一个 table 获得的数据。我的约束是三个字段:id_1、id_2 和日期。它的字段必须是唯一的。 如果我这样做:

ALTER TABLE my_table
ADD CONSTRAINT constr_1 UNIQUE (id_1, id_2, date);

然后:

insert into my_table
(id_1, id_2, quantity, date)
values (1, null, 5, '2022-04-27'),   -- values I get another select request
       (null, 5, 5, '2022-04-27'),  -- this means that the values can be different
       (99, 85, 100, '2022-04-29')
ON CONFLICT (id_1, id_2, date)
DO Update
SET quantity = excluded.quantity

约束不起作用,我只是插入具有相同值的新行。如何在没有记录的情况下插入并在有记录的情况下更新?

PostgreSQL 将 NULL 视为不同的值,因此,您可以在具有 UNIQUE 索引的列中有多个 NULL 值。当您为 table 定义主键或唯一约束时,PostgreSQL 会自动创建相应的 UNIQUE 索引。
解决方案是创建一个具有合并的唯一索引。在此示例中,我使用了 coalesce(~, 0),这意味着 null 和 0 被视为同一事物。您可能更喜欢使用其他值,例如 int 的最大可能值 2147483648.Please 而不是我们必须修改 ON CONFLICT 列表以匹配索引。

CREATE temp TABLE my_table (
    id_1 int,
    id_2 int,
    quantity numeric,
    mytable_date date
);
CREATE UNIQUE INDEX
my_table_unique ON my_table
(coalesce(id_1,0), coalesce(id_2,0), coalesce(mytable_date,'1900-01-01'));
INSERT INTO my_table (id_1, id_2, quantity, mytable_date)
    VALUES (1, NULL, 5, '2022-04-27'), (NULL, 5, 5, '2022-04-27'), (99, 85, 100, '2022-04-29')
ON CONFLICT (coalesce(id_1,0), coalesce(id_2,0), coalesce(mytable_date,'1900-01-01'))
    DO UPDATE SET
        quantity = excluded.quantity;

INSERT INTO my_table (id_1, id_2, quantity, mytable_date)
    VALUES (99, 85, 101, '2022-04-29')
ON CONFLICT (coalesce(id_1,0), coalesce(id_2,0), coalesce(mytable_date,'1900-01-01'))
    DO UPDATE SET
        quantity = excluded.quantity;

3 行受影响

1 行受影响

select * from my_table;
id_1 | id_2 | quantity | mytable_date
---: | ---: | -------: | :-----------
   1 | null |        5 | 2022-04-27  
null |    5 |        5 | 2022-04-27  
  99 |   85 |      101 | 2022-04-29  

*db<>fiddle here74bf159a4d041c31fec5f)

CREATE temp TABLE my_table (
    id_1 int,
    id_2 int,
    quantity numeric,
    mytable_date date
);

ALTER TABLE my_table
    ADD CONSTRAINT constr_1 UNIQUE (id_1, id_2, mytable_date);

然后

INSERT INTO my_table (id_1, id_2, quantity, mytable_date)
    VALUES (1, NULL, 5, '2022-04-27'), (NULL, 5, 5, '2022-04-27'), (99, 85, 100, '2022-04-29')
ON CONFLICT (id_1, id_2, mytable_date)
    DO UPDATE SET
        quantity = excluded.quantity;

INSERT INTO my_table (id_1, id_2, quantity, mytable_date)
    VALUES (99, 85, 101, '2022-04-29')
ON CONFLICT (id_1, id_2, mytable_date)
    DO UPDATE SET
        quantity = excluded.quantity;

请查看手册部分:5.4.3。关于特殊情况的唯一约束null.

In general, a unique constraint is violated if there is more than one row in the table where the values of all of the columns included in the constraint are equal. However, two null values are never considered equal in this comparison. That means even in the presence of a unique constraint it is possible to store duplicate rows that contain a null value in at least one of the constrained columns. This behavior conforms to the SQL standard, but we have heard that other SQL databases might not follow this rule. So be careful when developing applications that are intended to be portable.