【问题标题】:Delete with Join in MySQL在 MySQL 中使用 Join 删除
【发布时间】:2010-10-13 18:12:15
【问题描述】:

这是创建我的表格的脚本:

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的帖子。

目前无法使用,因为没有删除任何帖子。

【问题讨论】:

  • 我认为 Yehosef 的答案应该是被接受的,因为他按照你的要求使用 Join 并且它比使用 yukondude 建议的 IN 子句表现更好......
  • 首选模式是 DELETE posts FROM posts JOIN projects ...,而不是 IN (subquery) 模式。 (来自 Yehosef 的回答给出了首选模式的示例。)
  • @GerardoGrignoli,它对特定引擎或 MySQL 版本的性能更好?这两个查询没有理由执行任何不同的操作,因为 AFAIK 它们是相同的。当然,如果每次我的查询优化器做一些愚蠢的事情时我都能得到一分钱......
  • 你也可以使用alias作为表名并使用它。
  • 只有傻瓜才会认为他们可以通过查看查询来预测性能。看起来比执行难度高 10 倍的查询可能会快得多。这很大程度上取决于键。

标签: mysql


【解决方案1】:

这个我比较习惯子查询的解决方案,但是我在MySQL中没有尝试过:

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

【讨论】:

  • 你真的应该避免在 SQL 中使用 IN 关键字(尽管它通常对初学者来说更容易理解),而是使用 JOIN 代替(如果可能的话),因为子查询通常会使事情变慢。
  • 当子查询返回大量行时,这往往会导致数据库崩溃。它也非常慢。
  • @yukondude 的确,“IN”比“JOIN”更容易理解,这就是为什么不真正熟悉 SQL 的人最终会到处写“IN”,而他们可以使用性能更好的“JOIN”(或者更好,取决于查询)。我记得几年前,我几乎所有的 SQL 查询都会由真正知道如何编写好的查询的人重写。这就是我添加注释以避免“IN”的原因,这样人们就会知道,如果可能的话,他们应该避免使用它。
  • 我来到这个页面的真正原因是因为我用 IN 语句编写的查询很慢。绝对避免这里接受的答案。
  • 这个问题由来已久,重要的是要知道为什么应该使用 JOIN 而不是 IN。当 where 条件在一行上运行时,它将在 IN 中运行该查询。这意味着,如果需要根据 WHERE 检查 100 行,则该子查询将运行 100 次。而 JOIN 只会运行一次。因此,随着您的数据库变得越来越大,该查询将需要越来越长的时间才能完成。 @markus 仅仅因为某些东西不重要并不意味着您应该编写糟糕的代码。写得好一点会为你节省很多时间和将来的头痛。 :)
【解决方案2】:

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

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

编辑:有关更多信息,您可以查看this alternative answer

【讨论】:

  • 需要注意的是,这是正确的答案,因为连接迫使您使用“DELETE posts FROM posts”而不是普通的“DELETE FROM posts”,因为要删除的表不再是明确的.谢谢!
  • 请注意,您不能在此处使用 'as' 方法,例如,在 p.project_id 上将项目内部连接为 p ...
  • 实际上您可以为连接表使用别名,但不能为主表(帖子)使用别名。 "DELETE posts FROM posts INNER JOIN projects p ON p.project_id = posts.project_id"
  • 这是最好的答案,因为您甚至可以一次性从两个表中删除DELETE posts , projects FROM posts INNER JOIN projects ON projects.project_id = posts.project_id WHERE projects.client_id = :client_id
【解决方案3】:

或者同样的东西,只是语法略有不同(IMO 更友好):

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

顺便说一句,mysql 使用连接几乎总是比子查询快...

【讨论】:

  • USING是什么意思?
  • @bigtex777:请注意,SELECT 语句中的关键字 USING 与 DELETE 语句中的相同关键字几乎没有关系。在 SELECT 中它指定要加入的列的列表,而在 DELETE 中它是一个连接中所有表的列表
【解决方案4】:

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

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

使用它而不是连接的一个原因是DELETEJOIN 禁止使用LIMIT。如果你想在块中删除以免产生全表锁,你可以添加LIMIT使用这个DELETE WHERE EXISTS方法。

【讨论】:

  • 这个查询可以写成“别名”吗?从语法中不太清楚 EXISTS() 中的 posts 如何与删除行的 posts 相同。 (恕我直言)
  • 我听到了,但表中不允许删除别名。子查询中的“posts”必须是完整的表名,这意味着如果您想在子选择 From 子句中重用该表,则必须在此处为其命名。
  • 这工作:DELETE p FROM posts p WHERE EXISTS ( SELECT 1 FROM projects WHERE projects.client_id = p.client_id);
  • 在我的例子中,exists/in() 查询慢了 1000+ 倍。所以当然,你可以使用 limit ,但是,如果它需要你 1000 倍的时间来执行,它就毫无意义。
【解决方案5】:

尝试如下:

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

【讨论】:

    【解决方案6】:

    如果加入对您不起作用,您可以尝试此解决方案。用于在不使用外键 + 特定 where 条件时从 t1 中删除孤立记录。 IE。它从 table1 中删除记录,这些记录具有空字段“code”,并且 table2 中没有记录,按字段“name”匹配。

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

    【讨论】:

      【解决方案7】:

      您也可以像这样使用 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
      

      【讨论】:

      • 这对复合键连接很有用,以避免重复表名
      • "实际上你可以为连接的表使用别名,但不能用于主表(posts)。'DELETE posts FROM posts INNER JOIN projects p ON p.project_id = posts.project_id'" —@网路
      • 其实(引用自dev.mysql.com/doc/refman/5.0/en/delete.html)“如果为表声明别名,则在引用表时必须使用别名:DELETE t1 FROM test AS t1, test2 WHERE ...”所以使用别名就可以了。
      【解决方案8】:

      由于您选择了多个表,因此要从中删除的表不再是明确的。你需要选择

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

      在这种情况下,table_name1table_name2 是同一个表,所以这将起作用:

      DELETE projects FROM posts INNER JOIN [...]
      

      如果您愿意,您甚至可以从两个表中删除:

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

      注意order bylimit don't work for multi-table deletes

      另外请注意,如果为表声明别名,则在引用该表时必须使用别名:

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

      Contributions from Carpetsmoker and etc.

      【讨论】:

      • @Yehosef,有一群人觉得 CAPS 真的很刺眼。我相信我不是唯一一个,我见过不少人也采用小写风格。
      • 很公平 - 我尊重你以你喜欢的风格写下答案的权利;)
      • 你真的不喜欢大写的关键字,不是吗? :D 我刚看到你写<!doctype..>,而其他人会写<!DOCTYPE..>^^ 但是你的回答仍然对我有帮助,所以请接受我的投票:)
      • 添加到大写/无大写讨论中,确实,您可以使用您喜欢的任何样式,但在您的回答中,您实际上是在您觉得方便的地方混合样式 - ON 大写.对于经验不足的开发人员,它可能会传达出在风格方面凌乱和不一致是可以的。
      • 大写关键字不明显。他们正在使查询可读;)
      【解决方案9】:
      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);
      

      【讨论】:

        【解决方案10】:

        使用 JOIN 删除 MySQL 记录

        您通常在 SELECT 语句中使用 INNER JOIN 从一个表中选择在其他表中有相应记录的记录。我们还可以使用带有 DELETE 语句的 INNER JOIN 子句从表中删除记录以及其他表中的相应记录,例如,从 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】:

          试试这个,

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

          【讨论】:

            【解决方案12】:

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

            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
            

            【讨论】:

              【解决方案13】:

              单表删除:

              为了从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
              

              【讨论】:

                【解决方案14】:

                一种解决方案是使用子查询

                DELETE FROM posts WHERE post_id in (SELECT post_id FROM posts p
                INNER JOIN projects prj ON p.project_id = prj.project_id 
                INNER JOIN clients c on prj.client_id = c.client_id WHERE c.client_id = :client_id 
                );
                

                子查询返回需要删除的ID;所有三个表都使用连接连接,并且仅删除满足过滤条件的记录(在您的情况下,即 where 子句中的 client_id)。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-02-15
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多