Удаление самореферентным внешним ключом в Postgres - PullRequest
0 голосов
/ 30 мая 2018

С таблицей с внешним ссылочным ключом:

CREATE TABLE tree (  
    id INTEGER,
    parent_id INTEGER,
    PRIMARY KEY (id)  
);

ALTER TABLE tree 
   ADD CONSTRAINT fk_tree
   FOREIGN KEY (parent_id) 
   REFERENCES tree(id);

INSERT INTO tree (id, parent_id)
VALUES (1, null),
       (2, 1),
       (3, 1),
       (4, 2),
       (5, null),
       (6, 5);

Я хочу удалить ветку путем рекурсивного обхода дерева, поскольку я не могу использовать ON DELETE CASCADE.

WITH RECURSIVE branch (id, parent_id) AS (
      SELECT id, parent_id
      FROM tree
      WHERE id = 1 -- Delete branch with root id = 1

      UNION ALL SELECT c.id, c.parent_id
      FROM tree c -- child
      JOIN branch p -- parent
            ON c.parent_id = p.id
)
DELETE FROM tree t
USING branch b
WHERE t.id = b.id;

Безопасно ли делать это с помощью общего табличного выражения в Postgres или мне нужно беспокоиться о порядке удаления записей?Будет ли Postgres удалять все строки как один набор или по одному?

Если ответ зависит от версии, от какой версии безопасно удаление?

1 Ответ

0 голосов
/ 30 мая 2018

Нет, вам не нужно беспокоиться о порядке выбора.

Внешние ключи (в отличие от уникальных ограничений) оцениваются на оператор , а не на строку.И общее табличное выражение по-прежнему является одиночным оператором , даже если в нем несколько SELECT и DELETE.

Таким образом, если все ограничения по-прежнему действительны после завершения оператора, все в порядке.


Это легко увидеть с помощью следующего простого теста:

CREATE TABLE fk_test
(
  id          integer PRIMARY KEY,
  parent_id   integer,
  FOREIGN KEY (parent_id) REFERENCES fk_test (id)
);

INSERT INTO fk_test (id, parent_id) 
VALUES 
  (1, null),
  (2, 1),
  (3, 2),
  (4, 1);

Таким образом, очевидно, что работает следующее, даже если идентификаторы указаны в «неправильном» порядке:

DELETE FROM fk_test
WHERE id IN (1,2,3,4);

Следующая также работает - показывает, что CTE все еще является одним оператором:

with c1 as (
  delete from fk_test where id = 1
), c2 as (
  delete from fk_test where id = 2
), c3 as (
  delete from fk_test where id = 3
)
delete from fk_test where id = 4;
...