在以下语句完成之前,不提交CTE删除

时间:2018-10-11 10:20:31

标签: sql postgresql common-table-expression sql-delete

我遇到的问题是,删除的数据稍后仍会出现在同一查询中。自然,在完全独立的查询中,不会显示已删除的数据。

这不是我的用例,但是我认为这是显示问题的最简单方法:

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;

我把它放在小提琴here中。

似乎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;

还有更新的fiddle

您可以看到该公司未被删除。

4 个答案:

答案 0 :(得分:1)

这是预期的并有记录。

Quote from the manual

  

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子查询中排除刚删除的员工很重要,因为这在第二个delete语句中始终可见,因此不存在永远不会为真。因此,子查询从本质上检查是否存在除分配给同一公司的已删除员工以外的其他员工。

在线示例:https://rextester.com/IVZ78695

答案 1 :(得分:0)

您快到了!

尝试以下方法:

private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        try
        {

            String connectionString = "Data Source=DWH; User Id=readonly; Password=********;";
            OracleConnection con = new OracleConnection();
            con.ConnectionString = connectionString;
            con.Open();
            OracleCommand cmd = new OracleCommand();
            cmd.CommandText = "SELECT * FROM SALARIES";
            cmd.Connection = con;
            OracleDataReader dr = cmd.ExecuteReader();
            DataGrid dg = new DataGrid();
            dr.Read();
        }
        catch (Exception exp) { }
    }

编辑:您不需要CTE,只需使用带有RETURNING *的DELETE语句即可。 :-)

答案 2 :(得分:0)

您的查询需要从CTE中进行选择。
这样的结果应该与@johey答案相同。

WITH
  deleted_employee AS (
    DELETE FROM
      employee
     WHERE
       id = 1
     RETURNING
       *
  )
SELECT
  *
FROM
 deleted_employee

答案 3 :(得分:0)

我已经设法解决了我在问题编辑中描述的确切问题:

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;

更新了fiddle

从本质上讲,这只是一种黑客,它指示第二个DELETE仅在原始快照中恰好有一个与该公司关联的员工时才对受影响的公司采取行动。

虽然这很糟糕,但我不会使用。