【问题标题】:PostgreSQL Upsert with a WHERE clause带有 WHERE 子句的 PostgreSQL Upsert
【发布时间】:2018-05-26 23:27:11
【问题描述】:

我正在尝试将 Oracle 合并查询迁移到 PostgreSql。如this 文章中所述,Postgres UPSERT 语法支持“where 子句”来识别冲突条件。

很遗憾,该网页没有提供带有“where 子句”的示例。我尝试在其他地方搜索它,但找不到它。因此提出了这个问题。

按照上面给出的网页中的相同示例,这里是一个示例设置:

CREATE TABLE customers (
    customer_id serial PRIMARY KEY,
    name VARCHAR UNIQUE,
    email VARCHAR NOT NULL,
    active bool NOT NULL DEFAULT TRUE
);

INSERT INTO customers (NAME, email) VALUES
 ('IBM', 'contact@ibm.com'),
 ('Microsoft', 'contact@microsoft.com'),
 ('Intel','contact@intel.com');


SELECT * FROM customers;
 customer_id |   name    |         email         | active
-------------+-----------+-----------------------+--------
           1 | IBM       | contact@ibm.com       | t
           2 | Microsoft | contact@microsoft.com | t
           3 | Intel     | contact@intel.com     | t
(3 rows)

我希望我的 UPSERT 语句看起来像这样:

INSERT INTO customers (NAME, email)
VALUES
('Microsoft', 'hotline@microsoft.com') 
ON CONFLICT where (name = 'Microsoft' and active = TRUE)
DO UPDATE SET email = 'hotline@microsoft.com';

这个例子有点做作,但我希望我能够在这里传达要点。

【问题讨论】:

    标签: sql postgresql indexing unique upsert


    【解决方案1】:

    您需要一个部分索引。删除列 name 上的 uniqe 约束并在列上创建部分索引:

    CREATE TABLE customers (
        customer_id serial PRIMARY KEY,
        name VARCHAR,
        email VARCHAR NOT NULL,
        active bool NOT NULL DEFAULT TRUE
    );
    
    CREATE UNIQUE INDEX ON customers (name) WHERE active;
    
    INSERT INTO customers (NAME, email) VALUES
     ('IBM', 'contact@ibm.com'),
     ('Microsoft', 'contact@microsoft.com'),
     ('Intel','contact@intel.com');
    

    查询应如下所示:

    INSERT INTO customers (name, email)
    VALUES
        ('Microsoft', 'hotline@microsoft.com') 
    ON CONFLICT (name) WHERE active
    DO UPDATE SET email = excluded.email;
    
    SELECT *
    FROM customers;
    
     customer_id |   name    |         email         | active 
    -------------+-----------+-----------------------+--------
               1 | IBM       | contact@ibm.com       | t
               3 | Intel     | contact@intel.com     | t
               2 | Microsoft | hotline@microsoft.com | t
    (3 rows)    
    

    注意特殊记录excluded.the documentation:的正确使用

    ON CONFLICT DO UPDATE 中的 SET 和 WHERE 子句可以使用表的名称(或别名)访问现有行,并使用特殊的 excluded 表访问建议插入的行。

    【讨论】:

    • 作为一种更清晰的替代方法,您可以创建一个命名的唯一约束,并使用ON CONFLICT ON CONSTRAINT my_constraint_name。据我了解,(name) WHERE active 子句仅用于推断要使用的约束,并且如果约束定义很复杂,最终可能会比精心选择的约束名称可读性差。
    • @IMSoP - 您不能在部分唯一索引上创建约束。另一方面,在on conflict 中使用列名非常清楚。
    • 啊,公平点,我没有意识到这一点。我对清晰度的想法是,约束名称可以描述意图,而不是枚举机制,在某些情况下会使查询更具可读性。但无论如何,在这种情况下似乎别无选择。
    猜你喜欢
    • 2018-11-12
    • 2017-11-18
    • 2016-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 2011-07-23
    相关资源
    最近更新 更多