Почему PostgreSQL CTE с DELETE не работает? - PullRequest
0 голосов
/ 21 октября 2018

Я пытался удалить запись из моей таблицы запасов, если обновление в той же таблице приводит к количеству 0 с использованием двух CTE.Upserts работают, но удаление не генерирует результат, который я ожидал.количество в таблице запасов меняется на ноль, но запись не удаляется.Структура таблицы:

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;

Кто-нибудь знает, что происходит?

Это пример того, что я 'm пытается сделать:

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, как только количество будет обновлено до нуля.

Имеет смысл?

1 Ответ

0 голосов
/ 22 октября 2018

Здесь DELETE работает так, как было задумано для работы.Ответ на самом деле довольно прост и задокументирован.Я испытывал такое же поведение много лет назад.

Причина, по которой вы фактически не удаляете данные, заключается в том, что условие вашего местоположения не совпадает с тем, что хранится в таблице, и тем, что видит оператор удаления.

Все вложенные операторы в CTE (общее табличное выражение) выполняются с с одним и тем же снимком данных, поэтому они не могут видеть эффект другого оператора вцелевой стол.В этом случае, когда вы запускаете UPDATE, а затем DELETE, оператор DELETE видит те же данные, что и UPDATE, и не видит обновленных данных, которые были изменены оператором UPDATE.

Как вы можете обойти это? Вам нужно разделить UPDATE & DELETE на два независимых утверждения.

Если вам нужно передать информацию о том, что удалять, вы можете, например, (1) создать временную таблицу и вставить первичный ключ данных, который был обновлен, чтобы вы могли присоединитьсяк этому в вашем последнем запросе (УДАЛИТЬ на основе данных, которые были ОБНОВЛЕНЫ). (2) Вы можете достичь того же результата, просто добавив столбец в обновленную таблицу и изменив его значение, чтобы пометить обновленные строки или (3) , однако вам понравится получить работусделанный.Вы должны почувствовать, что нужно сделать, с помощью приведенных выше примеров.

Цитирую руководство, чтобы подтвердить мои выводы: 7.8.2.Операторы, изменяющие данные в WITH

Подвыражения в WITH выполняются одновременно друг с другом и с основным запросом.Следовательно, при использовании операторов изменения данных в WITH порядок, в котором фактически происходят указанные обновления, непредсказуем. Все операторы выполняются с одним и тем же снимком (см. Главу 13), поэтому они не могут «видеть» влияния друг друга на целевые таблицы.

(...) Это также относится к удалению строки, которая уже была обновлена ​​в том же операторе: выполняется только обновление

...