在MySQL中加入联接删除


501

这是创建我的表的脚本:

CREATE TABLE clients (
   client_i INT(11),
   PRIMARY KEY (client_id)
);
CREATE TABLE projects (
   project_id INT(11) UNSIGNED,
   client_id INT(11) UNSIGNED,
   PRIMARY KEY (project_id)
);
CREATE TABLE posts (
   post_id INT(11) UNSIGNED,
   project_id INT(11) UNSIGNED,
   PRIMARY KEY (post_id)
);

在我的PHP代码中,删除客户端时,我要删除所有项目帖子:

DELETE 
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id;

posts表client_id仅没有外键project_id。我想删除已通过项目中的帖子client_id

现在无法正常工作,因为没有帖子被删除。


10
我认为Yehosef的答案应该被接受,因为他按照您的要求使用Join,并且它的效果比yukondude建议的使用IN子句还要好...
Gerardo Grignoli14年

2
首选模式是DELETE posts FROM posts JOIN projects ...,而不是IN (subquery)模式。(来自Yehosef的答案给出了首选模式的示例。)
spencer7593 2015年

@GerardoGrignoli,对于特定的引擎或MySQL版本,它的性能更好吗?没有理由为什么两个查询的执行结果应该有所不同,因为AFAIK相同。当然,如果我有我的每次查询优化镍做了一件愚蠢的....
保罗·德雷珀

您也可以使用alias表名并使用它。
biniam '16

Answers:


1253

您只需要指定要从posts表中删除条目:

DELETE posts
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

编辑:有关更多信息,您可以看到此替代答案


117
应当指出,这是正确的答案,因为联接将迫使您使用“ DELETE FROM FROM posts”而不是常规的“ DELETE FROM posts”,因为要删除的表不再是唯一的。谢谢!
siannopollo

8
请注意,您不能在此处使用“ as”方法,例如,内部连接项目在p.project_id上为p ...
zzapper 2012年

14
实际上,您可以对联接表使用别名,但不能对主表(帖子)使用别名。“从帖子INNER JOIN项目中删除帖子p ON p.project_id = posts.project_id”
Weboide 2012年

85
“如果为表声明别名,则在引用表时必须使用别名”,例如DELETE d FROM posts AS d JOIN projects AS p ON ...
KCD 2012年

14
这是最佳答案,因为您甚至可以在一个操作中从两个表中删除DELETE posts , projects FROM posts INNER JOIN projects ON projects.project_id = posts.project_id WHERE projects.client_id = :client_id
Developerium

84

由于您正在选择多个表,因此要删除的表不再是唯一的。您需要选择

DELETE posts FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

在这种情况下,table_name1并且table_name2是相同的表,因此可以使用:

DELETE projects FROM posts INNER JOIN [...]

您甚至可以从两个表中删除:

DELETE posts, projects FROM posts INNER JOIN [...]

请注意,order by并且limit 不适用于多表删除

还应注意,如果为表声明别名,则在引用表时必须使用别名:

DELETE p FROM posts as p INNER JOIN [...]

来自Carpetsmoker等的贡献


3
如果您要提供更好的答案-至少可以使SQL更具可读性。此问题中的所有其他SQL参考都具有大写的关键字(具有0票的关键字除外)。我不确定为什么您要还原我的修改。
Yehosef

3
@Yehosef,有一群人发现CAPS确实很刺眼。我相信我不是唯一的一个,我也看到很多人也使用小写字母。
Pacerier,2015年

1
足够公平-我尊重您以您喜欢的方式写您的答案的权利;)
Yehosef 2015年

7
没错,要添加到大写/无大写讨论中,可以使用任何喜欢的样式,但是在您的答案中,实际上是在您认为方便的地方混合使用了样式- ON大写。对于经验不足的开发人员,它可能表示,在样式方面可以做到凌乱且不一致。
阴影

16
CAPS关键字不刺眼。他们正在使查询变得可读;)
萨赫姆(Sachem

50

还是同一件事,只是语法略有不同(IMO更友好):

DELETE FROM posts 
USING posts, projects 
WHERE projects.project_id = posts.project_id AND projects.client_id = :client_id;

顺便说一句,使用mysql的mysql几乎总是比子查询更快的方式...


使用是什么意思?
CMCDragonkai 2015年


10
@ bigtex777:请注意,SELECT语句中的关键字USING与DELETE语句中的相同关键字无关。在SELECTs中,它指定要连接的列的列表,而在DELETE中,它是连接中所有表的列表
ivanhoe 2015年

42

您也可以像这样使用ALIAS,它可以在我的数据库中使用!t是需要删除的表!

DELETE t FROM posts t
INNER JOIN projects p ON t.project_id = p.project_id
AND t.client_id = p.client_id

1
这对于复合键联接很有用,以避免重复表名
Marquez 2014年

1
“实际上,您可以对联接表使用别名,但不能对主表(帖子)使用别名。'从帖子INNER JOIN项目中删除帖子p ON p.project_id = posts.project_id'” — @ Weboide
Jeaf Gilbert,

2
实际上(引用dev.mysql.com/doc/refman/5.0/en/delete.html的引用)“如果为表声明别名,则在引用表时必须使用别名:DELETE t1 FROM test AS t1, test2 WHERE ...”,因此可以使用别名。
彼得·鲍尔斯

25

我更习惯于此的子查询解决方案,但是我还没有在MySQL中尝试过:

DELETE  FROM posts
WHERE   project_id IN (
            SELECT  project_id
            FROM    projects
            WHERE   client_id = :client_id
        );

85
您实际上应该避免在SQL中使用IN关键字(即使对于初学者来说通常更容易理解),而应尽可能使用JOIN(因为可能),因为子查询通常会使事情变慢。
2011年

14
当子查询返回大量行时,这往往会使数据库崩溃。它也非常慢。
拉吉2012年

2
@yukondude确实,“ IN”比起初的“ JOIN”要容易理解得多,这就是为什么对SQL不太熟悉的人最终会到处写“ IN”,而他们可以使用性能更好的“ JOIN”(或更好的whooole,具体取决于查询)。我记得几年前,几乎所有我的SQL查询都将由真正知道如何编写好的查询的人重写。这就是为什么我添加注释以避免“ IN”的原因,以便使人们知道,如果可能的话,他们应该避免使用它。
user276648 2012年

10
我来到此页面的根本原因是因为我使用IN语句编写的查询非常慢。绝对避免在这里接受答案。
MikeKulls

4
与这个问题一样古老,重要的是要知道为什么要使用JOIN而不是IN。当条件连续运行时,它将在IN内运行该查询。这意味着,如果需要根据WHERE检查100行,则该子查询将运行100次。而JOIN仅运行一次。因此,随着您的数据库越来越大,该查询将花费越来越长的时间才能完成。@markus仅仅因为不重要的事情并不意味着您应该编写错误的代码。更好地编写它可以为您节省很多时间和将来的头痛。:)
RisingSun

11

MySQL用JOIN删除记录

通常,您可以在SELECT语句中使用INNER JOIN从表中选择在其他表中具有相应记录的记录。我们还可以将INNER JOIN子句与DELETE语句一起使用,以从表中删除记录以及其他表中的对应记录,例如,从满足特定条件的T1和T2表中删除记录,请使用以下语句:

DELETE T1, T2
FROM T1
INNER JOIN T2 ON T1.key = T2.key
WHERE condition

请注意,您将表名称T1和T2放在了DELETE和FROM之间。如果省略T1表,则DELETE语句仅删除T2表中的记录,而如果省略T2表,则仅删除T1表中的记录。

连接条件T1.key = T2.key指定T2表中需要删除的相应记录。

WHERE子句中的条件指定T1和T2中的哪些记录需要删除。


11

单表删除:

为了从posts表中删除条目:

DELETE ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

为了从projects表中删除条目:

DELETE pj 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

为了从clients表中删除条目:

DELETE C
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

多表删除:

为了从联接结果中删除多个表中的条目,您需要在表名后指定DELETE逗号分隔列表:

假设你想从所有三个表(删除条目postsprojectsclients)为特定的客户端:

DELETE C,pj,ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id

7

尝试如下:

DELETE posts.*,projects.* 
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id;

4

使用子选择比使用更好的另一种删除方法INWHERE EXISTS

DELETE  FROM posts
WHERE   EXISTS ( SELECT  1 
                 FROM    projects
                 WHERE   projects.client_id = posts.client_id);

使用此代替连接的原因之一是DELETEwith JOIN禁止使用LIMIT。如果希望删除块以免产生完整的表锁,则可以LIMIT使用此DELETE WHERE EXISTS方法添加。


1
可以使用“别名”编写此查询吗?从语法上还不清楚,postsEXISTS()中的内容与posts删除行的方式是否相同。(无论如何,恕我直言)
MattBianco 2014年

我听到了您的声音,但是不允许在表中删除别名。子查询中的“ posts”必须是完整的表名,这意味着,如果要在子选择From子句中重新使用该表,则必须在该表中使用别名。
Jim Clouse 2014年

1
这有效:DELETE p FROM posts p WHERE EXISTS ( SELECT 1 FROM projects WHERE projects.client_id = p.client_id);
MattBianco 2014年

4
mysql> INSERT INTO tb1 VALUES(1,1),(2,2),(3,3),(6,60),(7,70),(8,80);

mysql> INSERT INTO tb2 VALUES(1,1),(2,2),(3,3),(4,40),(5,50),(9,90);

从一个表中删除记录:

mysql> DELETE tb1 FROM tb1,tb2 WHERE tb1.id= tb2.id;

从两个表中删除记录:

mysql> DELETE tb2,tb1 FROM tb2 JOIN tb1 USING(id);

1

如果加入对您不起作用,则可以尝试此解决方案。它用于在不使用外键+特定条件的情况下从t1删除孤立记录。即,它从表1中删除具有空字段“代码”且在表2中没有记录的记录,并按字段“名称”进行匹配。

delete table1 from table1 t1 
    where  t1.code = '' 
    and 0=(select count(t2.name) from table2 t2 where t2.name=t1.name);

0

尝试这个,

DELETE posts.*
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

-3

-请注意,您不能在需要删除的表上使用别名

DELETE tbl_pagos_activos_usuario
FROM tbl_pagos_activos_usuario, tbl_usuarios b, tbl_facturas c
Where tbl_pagos_activos_usuario.usuario=b.cedula
and tbl_pagos_activos_usuario.cod=c.cod
and tbl_pagos_activos_usuario.rif=c.identificador
and tbl_pagos_activos_usuario.usuario=c.pay_for
and tbl_pagos_activos_usuario.nconfppto=c.nconfppto
and NOT ISNULL(tbl_pagos_activos_usuario.nconfppto)
and c.estatus=50
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.