sql - 为什么带有 DELETE 的 PostgreSQL CTE 不起作用?

标签 sql postgresql common-table-expression sql-delete

如果同一表中的更新导致使用两个 CTE 的数量为 0,我试图从我的库存表中删除一条记录。 upserts 正在工作,但 delete 没有产生我期望的结果。库存表中的数量正在更改为零,但记录未被删除。 表结构:

CREATE TABLE IF NOT EXISTS stock_location (
    stock_location_id SERIAL
    , site_code VARCHAR(10) NOT NULL
    , location_code VARCHAR(50) NOT NULL
    , status CHAR(1) NOT NULL DEFAULT 'A'
    , CONSTRAINT pk_stock_location PRIMARY KEY (stock_location_id)
    , CONSTRAINT ui_stock_location__keys UNIQUE (site_code, location_code)
);

CREATE TABLE IF NOT EXISTS stock (
    stock_id SERIAL
    , stock_location_id INT NOT NULL
    , item_code VARCHAR(50) NOT NULL
    , quantity FLOAT NOT NULL
    , CONSTRAINT pk_stock PRIMARY KEY (stock_id)
    , CONSTRAINT ui_stock__keys UNIQUE (stock_location_id, item_code)
    , CONSTRAINT fk_stock__stock_location FOREIGN KEY (stock_location_id)
        REFERENCES stock_location (stock_location_id)
        ON DELETE CASCADE ON UPDATE CASCADE
);

语句是这样的:

WITH stock_location_upsert AS (
    INSERT INTO stock_location (
        site_code
        , location_code
        , status
    ) VALUES (
        inSiteCode
        , inLocationCode
        , inStatus
    )
    ON CONFLICT ON CONSTRAINT ui_stock_location__keys
        DO UPDATE SET
            status = inStatus
    RETURNING stock_location_id
)
, stock_upsert AS (
    INSERT INTO stock (
        stock_location_id
        , item_code
        , quantity
    )
    SELECT
        slo.stock_location_id
        , inItemCode
        , inQuantity
    FROM stock_location_upsert slo
    ON CONFLICT ON CONSTRAINT ui_stock__keys
        DO UPDATE SET
            quantity = stock.quantity + inQuantity
        RETURNING stock_id, quantity
)
DELETE FROM stock stk
USING stock_upsert stk2
WHERE stk.stock_id = stk2.stock_id
    AND stk.quantity = 0;

有人知道这是怎么回事吗?

这是我正在尝试做的一个例子:

DROP TABLE IF EXISTS test1;

CREATE TABLE IF  NOT EXISTS test1 (
    id serial
    , code VARCHAR(10) NOT NULL
    , description VARCHAR(100) NOT NULL
    , quantity INT NOT NULL
    , CONSTRAINT pk_test1 PRIMARY KEY (id)
    , CONSTRAINT ui_test1 UNIQUE (code)
);

-- UPSERT
WITH test1_upsert AS (
    INSERT INTO test1 (
        code, description, quantity
    ) VALUES (
        '01', 'DESC 01', 1
    ) 
    ON CONFLICT ON CONSTRAINT ui_test1 
        DO UPDATE SET
            description = 'DESC 02'
            , quantity = 0
    RETURNING test1.id, test1.quantity
)
DELETE FROM test1 
USING test1_upsert
WHERE test1.id = test1_upsert.id
    AND test1_upsert.quantity = 0;

UPSERT 命令第二次运行时,一旦数量更新为零,它应该从 test1 中删除记录。

有道理吗?

最佳答案

在这里,DELETE 以其设计的方式工作。答案实际上非常简单并且有据可查。几年前我也经历过同样的行为。

您的 delete 并没有真正删除数据的原因是,就 delete 语句而言,您的 where 条件与存储在表中的内容不匹配。

CTE(公用表表达式)中的所有子语句都使用数据的相同快照执行,因此它们看不到其他语句效果 在目标表上。在这种情况下,当您运行 UPDATE 然后运行 ​​DELETE 时,DELETE 语句看到的数据与 UPDATE 看到的相同,并且看不到 UPDATE 语句修改的更新数据。

您如何解决这个问题?您需要将 UPDATE 和 DELETE 分成两个独立的语句。

如果您需要传递有关要删除的内容的信息,例如您可以(1) 创建一个临时表并插入已更新的数据主键,以便您可以加入该表在您的后一个查询中(根据已更新的数据进行删除)。 (2) 您可以通过简单地在更新的表格中添加一列并更改其值以标记更新的行或 (3) 来获得相同的结果完成的工作。通过上面的例子,你应该能感觉到需要做什么。

引用手册来支持我的发现: <强> 7.8.2. Data-Modifying Statements in WITH

The sub-statements in WITH are executed concurrently with each other and with the main query. Therefore, when using data-modifying statements in WITH, the order in which the specified updates actually happen is unpredictable. All the statements are executed with the same snapshot (see Chapter 13), so they cannot “see” one another's effects on the target tables.

(...) This also applies to deleting a row that was already updated in the same statement: only the update is performed

关于sql - 为什么带有 DELETE 的 PostgreSQL CTE 不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52919715/

相关文章:

sql - 执行递归查询时检测到循环

sql - 如何使用 SQL 在可变时间段(每月、每年等)上分配收入?

postgresql - 在 WITH 子句中链接 sql 查询以最终执行更新

java - 显示数据库中的 JTable 和计算公式

mysql - 如何在 MySQL 的一列中乘以数字(数据)?

mysql - sql查询性能不佳

mysql : Check duplicate value of a column in 3 different tables

ruby-on-rails - Rails- Heroku 请安装 postgresql 适配器 : gem install activerecord-postgresql-adapter

database - 如何在 Postgres 中使用 Blob 数据类型

sql - 运算符不是唯一的。无法选择最佳候选运算符。您可能需要添加显式类型转换