Проверки внешнего ключа выполняются после оператора или после каждой модификации? - PullRequest
0 голосов
/ 27 апреля 2018

Я пытаюсь сделать запрос, как

WITH tmp (parent_id, child_id, parent_val, child_val) AS (
  VALUES (...)
),
ins_parent AS (
  INSERT INTO parent (parent_id, parent_val)
  SELECT DISTINCT parent_id, parent_val FROM tmp
)
INSERT INTO child (child_id, parent_id, child_val)
SELECT child_id, parent_id, child_val FROM tmp

где есть внешний ключ от потомка к родителю.

Мне известно, что CTE не обязательно выполняются перед основным запросом. Меня беспокоит, что сервер пытается вставить дочернюю строку перед соответствующей родительской строкой.

Мне кажется, вопрос в том, когда выполняются проверки FK - запускаются ли они после оператора (в этом случае это должно быть хорошо), или после каждой отдельной вставки (в этом случае это может потенциально вызвать ошибку)? Я не смог найти ответ на этот вопрос. (Я думаю, что FK НЕ ОБЯЗАТЕЛЬНЫЙ, потому что я не установил это явно.)

Если это может привести к ошибке, я думаю, что будет довольно легко переписать запрос, чтобы избежать его. (Возможно, ins_parent return parent_id и ... child_val FROM tmp JOIN ins_parent USING (parent_id).) Но я бы не стал делать это более сложным, чем необходимо.

1 Ответ

0 голосов
/ 30 апреля 2018

Я считаю, что это безопасно, независимо от того, в каком порядке выполняются CTE. Я думаю, что мне удалось заставить их работать не по порядку, и я не увидел никаких ошибок FK.

У меня фактически есть две родительские таблицы и запрос, который больше похож на

WITH tmp (parent1_id, parent2_id, child_id, parents_val, child_val) AS (
  VALUES (...)
),
ins_parent1 AS (
  INSERT INTO parent1 (parent1_id, parents_val)
  SELECT DISTINCT parent1_id, parents_val FROM tmp
)
ins_parent2 AS (
  INSERT INTO parent2 (parent2_id, parents_val)
  SELECT DISTINCT parent2_id, parents_val FROM tmp
)
INSERT INTO child (child_id, parent1_id, parent2_id, child_val)
SELECT child_id, parent1_id, parent2_id, child_val FROM tmp

Когда я проверяю план выполнения (EXPLAIN WITH tmp ...), он выглядит примерно так:

 Insert on child
   CTE tmp
     -> ...
   CTE ins_parent1
     -> ...
   CTE ins_parent2
     -> ...
   -> ...

, что, я думаю, означает, что все три CTE будут выполнены перед вставкой в ​​child.

Но я могу изменить порядок запроса, чтобы поместить child в CTE, а parent1 снаружи:

WITH tmp (parent1_id, parent2_id, child_id, parents_val, child_val) AS (
  VALUES (...)
),
ins_child AS (
  INSERT INTO child (child_id, parent1_id, parent2_id, child_val)
  SELECT child_id, parent1_id, parent2_id, child_val FROM tmp
)
ins_parent2 AS (
  INSERT INTO parent2 (parent2_id, parents_val)
  SELECT DISTINCT parent2_id, parents_val FROM tmp
)
INSERT INTO parent1 (parent1_id, parents_val)
SELECT DISTINCT parent1_id, parents_val FROM tmp

и план выполнения изменяется соответственно:

 Insert on parent1
   CTE tmp
     -> ...
   CTE ins_child
     -> ...
   CTE ins_parent2
     -> ...
   -> ...

так что теперь я думаю, что CTE для child выполняется до того, как что-либо будет вставлено в parent1.

Но я все еще не получаю ошибок FK с таким запросом, который заставляет меня думать, что любой порядок выполнения безопасен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...