CTE удаление не совершено, пока не будут выполнены следующие утверждения - PullRequest
0 голосов
/ 11 октября 2018

У меня проблема в том, что удаленные данные все еще появляются позже в том же запросе.Естественно, в совершенно отдельном запросе удаленные данные не отображаются.

Это не мой вариант использования, но я думаю, что это самый простой способ показать проблему:

CREATE TABLE company (id INT PRIMARY KEY, name TEXT);
CREATE TABLE employee (id INT PRIMARY KEY, company_id INT REFERENCES company(id), name TEXT);

INSERT INTO company VALUES (1, 'first company');
INSERT INTO company VALUES (2, 'second company');

INSERT INTO employee VALUES (1, 1, 'first employee');
INSERT INTO employee VALUES (2, 2, 'second employee');

-- this select can successfully query for the data which has just been deleted
WITH deleted_employee AS (DELETE FROM employee WHERE id = 1 RETURNING id)
SELECT id, name FROM employee JOIN deleted_employee USING (id);

-- this select shows it has been deleted
SELECT * FROM employee;

Я положил его в скрипку здесь .

Кажется, что DELETE просто не фиксируется, пока не завершится весь запрос, что кажется странным, так как приоритет требует, чтобыDELETE происходит до SELECT.

Есть ли способ достичь этого за один запрос?


Редактировать

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

Вот запрос, который я, хотя, и выполнил бы:

WITH affected_company AS (DELETE FROM employee WHERE id = 1 RETURNING company_id)
DELETE FROM company
USING affected_company
WHERE NOT EXISTS (
  SELECT 1
  FROM employee
  WHERE company_id = affected_company.company_id
);

SELECT * FROM company;
SELECT * FROM employee;

И обновленная скрипка .

Вы видите, что компания не удаляется.

Ответы [ 4 ]

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

Мне удалось решить именно ту проблему, которую я описал при редактировании моего вопроса:

WITH affected_company AS (DELETE FROM employee WHERE id = 1 RETURNING company_id)
DELETE FROM company WHERE id IN (
  SELECT company_id
  FROM employee
  JOIN affected_company USING(company_id)
  GROUP BY company_id
  HAVING COUNT(*) = 1
);

SELECT * FROM company;
SELECT * FROM employee;

Обновлено Скрипка .

По сути, это просто взломатьуказание второму DELETE действовать только в отношении затронутой компании, если на первоначальном снимке был ровно один сотрудник, связанный с этой компанией.

Это брутто, и я не буду его использовать.

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

Вы почти у цели!

Попробуйте вместо этого:

DELETE FROM employee WHERE id = 1 RETURNING *;

Редактировать: вам не нужен CTE, просто инструкция DELETE с RETURNING * делает свое дело.: -)

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

Ваш запрос должен быть выбран из CTE.Это должно дать те же результаты, что и @johey answer.

WITH
  deleted_employee AS (
    DELETE FROM
      employee
     WHERE
       id = 1
     RETURNING
       *
  )
SELECT
  *
FROM
 deleted_employee
0 голосов
/ 11 октября 2018

Это ожидается и задокументировано.

Цитата из руководства

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

(выделено мной)


Вы можете использовать цепочечные CTE для удаления компании:

with deleted_emp as (
  delete from employee 
  where id = 1 
  returning company_id, id as employee_id
)
delete from company
where id in (select company_id from deleted_emp) 
  and not exists (select * 
                  from employee e
                     join deleted_emp af 
                       on af.company_id = e.company_id 
                      and e.id <> af.employee_id) 

Важно исключитьтолько что удаленный сотрудник из подзапроса not exists, поскольку он всегда будет отображаться во втором операторе удаления и, следовательно, несуществование никогда не будет истинным.Таким образом, подзапрос по существу проверяет, есть ли сотрудник, отличный от удаленного, назначенный той же компании.

Онлайн пример: https://rextester.com/IVZ78695

...