问题
The problem I'm having is that deleted data still appears later in the same query. Naturally, in a completely separate query, the deleted data does not appear.
This isn't my use-case, but I think this it's the simplest way to show the problem:
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;
I've put it into a fiddle here.
It seems the DELETE
just isn't committed until the whole query has completed, which feels strange since the precedence requires that the DELETE
occurs before the SELECT
.
Is there any way to achieve this in a single query?
Edit
The answers have answered the direct problem. The underlying problem is to delete an employee then delete its associated company, if there are no more employees associated with that company.
Here's the query I though would do the trick:
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;
And an updated fiddle.
You can see the company isn't being deleted.
回答1:
This is expected and documented.
Quote from the manual
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 alleviates the effects of the unpredictability of the actual order of row updates, and means that RETURNING data is the only way to communicate changes between different WITH sub-statements and the main query
(emphasis mine)
You can use chained CTEs to delete the company:
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)
It's important to exclude the just deleted employee from the not exists
sub-query as that will always be visible in the second delete statement and thus the not exists would never be true. So the sub-query essentially checks if there is an employee other than the deleted one assigned to the same company.
Online example: https://rextester.com/IVZ78695
回答2:
You are almost there!
Try this instead:
DELETE FROM employee WHERE id = 1 RETURNING *;
Edit: you don't need the CTE, just the DELETE statement with RETURNING * does the trick. :-)
回答3:
Your query needs to be selecting from the CTE.
This should give the same results as @johey answer.
WITH
deleted_employee AS (
DELETE FROM
employee
WHERE
id = 1
RETURNING
*
)
SELECT
*
FROM
deleted_employee
回答4:
I've managed to solve the exact problem I described in the edit to my question:
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;
Updated fiddle.
It's essentially just a hack instructing the second DELETE
only to act on the affected company if there was exactly one employee associated with that company at the original snapshot.
It's gross though and I won't be using it.
来源:https://stackoverflow.com/questions/52757679/cte-delete-not-committed-until-following-statements-complete