使用Postgres中的左外连接删除

dwl*_*amb 30 postgresql

我正在将数据库从MySQL切换到Postgres SQL.在MySQL中工作的选择查询在Postgres中有效,但类似的删除查询却没有.

我有两个数据表,列出了某些备份文件所在的位置.现有数据(ed)和新数据(nd).此语法将挑选出现有数据,这些数据可能表明文件在现有数据表中的位置,将其与相等的文件名和路径进行匹配,但没有关于它在新数据中的位置的信息:

SELECT ed.id, ed.file_name, ed.cd_name, ed.path, nd.cd_name
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND 
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
Run Code Online (Sandbox Code Playgroud)

我希望使用以下语法运行删除查询:

DELETE ed
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND 
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
Run Code Online (Sandbox Code Playgroud)

我试过DELETE ed,DELETE ed.*两者都渲染syntax error at or near "ed".如果我尝试没有别名的类似错误ed.如果我尝试

DELETE FROM tv_episodes AS ed
LEFT  JOIN data AS nd.....
Run Code Online (Sandbox Code Playgroud)

Postgres发回来syntax error at or near "LEFT".

我很困难,并且使用特定于psql的连接在删除查询上找不到太多.

小智 52

正如其他人所说,你不能直接在DELETE语句中LEFT JOIN.但是,您可以使用USING语句自行连接目标表的主键,然后对该自联接表保持连接.

DELETE FROM tv_episodes
USING tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
   ed.file_name = nd.file_name AND 
   ed.path = nd.path
WHERE
   tv_episodes.id = ed.id AND
   ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
Run Code Online (Sandbox Code Playgroud)

请注意WHERE子句中tv_episodes.id上的自连接.这避免了上面提供的子查询路由.

  • 谢谢你.在我的情况下这适用于几个~100K行的表.在其他答案中建议的子解释方法的`explain`给出的"总成本"是337448542.97(!),花了9分多钟.该方法的成本为40464.59,耗时6.7秒. (6认同)
  • 我错过了您的“注释”,并意外地从生产网站上的表中删除了所有内容:(尤其对于来自MySQL或SQL Server的用户而言,这确实不是很明显 (5认同)
  • 这是一个奇怪的例子。数据表应该在 USING 子句中,并且根本没有左连接 (2认同)

dwl*_*amb 6

正如 bf2020 所指出的,postgres 在执行 DELETE 查询时不支持 JOIN。子查询的建议解决方案让我想到了解决方案。从上面优化 SELECT 查询并将其用作 DELETE 查询语句的子查询:

DELETE FROM tv_episodes 
WHERE id in (
    SELECT ed.id
    FROM tv_episodes AS ed
    LEFT OUTER JOIN data AS nd ON
    ed.file_name = nd.file_name AND 
    ed.path = nd.path
    WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL
);
Run Code Online (Sandbox Code Playgroud)

对于某些数据库系统,尤其是 MySQL,子查询通常会消耗时间和 CPU 资源,效率低下。根据我的经验,我尽量避免使用子查询,因为它效率低下,而且此类查询有时是磨练自己的技能(例如学习 JOIN 语法)的简单方法。

由于 postgre 不允许使用 join 进行删除查询,因此以上是有效的解决方案。

  • *Postgres 在执行 `DELETE` 查询时不支持 `JOIN`s*。废话,确实如此,尽管语法很奇怪。`从......使用......删除`。请参阅手册。但是,它不支持对要从中删除的表进行左连接,这是事实。 (3认同)

小智 6

使用DELETE... USING语法:

DELETE FROM tv_episodes USING data WHERE 
tv_episodes.file_name = data.file_name AND 
tv_episodes.path = data.path AND 
tv_episodes.cd_name = 'MediaLibraryDrive' AND 
data.cd_name IS NULL;
Run Code Online (Sandbox Code Playgroud)


bf2*_*020 3

代替

DELETE ed
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND 
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
Run Code Online (Sandbox Code Playgroud)

请尝试

DELETE FROM tv_episodes
WHERE cd_name = 'MediaLibraryDrive' AND 
  (tv_episodes.filename, tv_episodes.path IN
    (SELECT ed.filename, 
    ed.path 
    FROM tv_episodes AS ed 
    INNER JOIN data AS nd 
      ON ed.file_name = nd.file_name 
        AND ed.path = nd.path
    WHERE nd.cd_name IS NULL)
  )
  ;
Run Code Online (Sandbox Code Playgroud)

JOINDELETE根据 postgresql 文档,在查询中无效。您可能需要连接 IN 表达式的左右部分。