590

Here is the script to create my tables:

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)
);

In my PHP code, when deleting a client, I want to delete all projects posts:

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

The posts table does not have a foreign key client_id, only project_id. I want to delete the posts in projects that have the passed client_id.

This is not working right now because no posts are deleted.

octano
  • 851
  • 1
  • 10
  • 18
GeekJock
  • 11,066
  • 13
  • 43
  • 44
  • 13
    I think Yehosef answer should be the accepted one, since he uses Join as you asked and that it performs better than using an IN clause as yukondude proposed... – Gerardo Grignoli Apr 22 '14 at 19:34
  • 3
    The preferred pattern is a **`DELETE posts FROM posts JOIN projects ...`**, rather than an `IN (subquery)` pattern. (The answer from Yehosef gives an example of the preferred pattern.) – spencer7593 May 25 '15 at 17:41
  • 1
    @GerardoGrignoli, it performs better for a particular engine or version of MySQL? There's no reason why the two queries should perform any differently, since AFAIK they are identical. Of course, if I had a nickel for everytime my query optimizer did something stupid.... – Paul Draper Feb 12 '16 at 01:12
  • You can also use `alias` for the table name and use that. – biniam Jul 04 '16 at 15:54
  • Only fools think they can predict performance by looking at a query. A query that looks 10x more difficult to execute can be much faster. It depends a lot on the keys. – doug65536 Nov 25 '21 at 20:36

14 Answers14

1473

You just need to specify that you want to delete the entries from the posts table:

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

EDIT: For more information you can see this alternative answer

Community
  • 1
  • 1
Yehosef
  • 17,987
  • 7
  • 35
  • 56
  • 129
    It should be noted that this is the correct answer because the join forces you to use "DELETE posts FROM posts" instead of the normal "DELETE FROM posts", since the table to delete is no longer unambiguous. Thanks! – siannopollo Apr 12 '12 at 20:23
  • 10
    Note you cannot use the 'as' method here e.g inner join projects as p on p.project_id ... – zzapper Apr 26 '12 at 17:01
  • 17
    Actually you can use an alias for joined tables, but not for the main table (posts). "DELETE posts FROM posts INNER JOIN projects p ON p.project_id = posts.project_id" – Weboide May 29 '12 at 15:44
  • 97
    ["If you declare an alias for a table, you must use the alias when referring to the table"](http://dev.mysql.com/doc/refman/5.5/en/delete.html) like so `DELETE d FROM posts AS d JOIN projects AS p ON ...` – KCD Oct 30 '12 at 22:11
  • 2
    I have never used this ':' notation before. Can someone shed some light on this? projects.client_id = :client_id what is ':'? – ThinkCode Apr 03 '13 at 17:54
  • It's just a way to represent variables to be replaced/escaped - check out examples in http://www.php.net/manual/en/pdostatement.bindparam.php – Yehosef Apr 04 '13 at 08:50
  • 15
    this is the best answer because you can even delete from both tables in one action `DELETE posts , projects FROM posts INNER JOIN projects ON projects.project_id = posts.project_id WHERE projects.client_id = :client_id` – Developerium Apr 22 '14 at 09:52
  • @siannopollo Is "no longer unambiguous" the same as "ambiguous"? – Andrew Liu Oct 01 '14 at 01:28
  • @LionLiu oops, you're correct. I meant "the table to delete is no longer ambiguous". – siannopollo Oct 01 '14 at 15:06
  • 1
    Hint: LIMIT will not working for "Multi-Table Deletes". See: http://dev.mysql.com/doc/refman/5.6/en/delete.html – user706420 Feb 24 '15 at 18:15
  • 1
    @tinybyte, If `select *` means select all, why does `delete *` not work? Why do we have to explicitly name both of them? – Pacerier Mar 19 '15 at 14:15
  • @Pacerier see http://meta.stackoverflow.com/questions/288352/how-to-handle-significant-changes-to-my-answer and my comments there - I'm still thinking about it. – Yehosef Mar 21 '15 at 20:04
  • @Yehosef, Since you wanted us to post a new answer, I've done so. There's too much useful info there to be buried under a rollback. You can link to my post below from your post. – Pacerier Mar 23 '15 at 07:18
  • 1
    @Yehosef, Quoting from https://dev.mysql.com/doc/refman/5.6/en/delete.html: " For the second multiple-table syntax, only matching rows from the tables listed in the FROM clause (before the USING clause) are deleted". And the second multi-table syntax as they put forward is : `DELETE FROM t1, t2 USING t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;`. So why do we need to mention the table name just after `DELETE` ? – Istiaque Ahmed Nov 09 '17 at 12:06
  • @Pacerier -- with select *, the * refers to any and all columns. With delete you are deleting rows. You might say the * is implicit since it deletes all column data in the row that gets deleted.. – Gerard ONeill Dec 21 '17 at 22:51
  • @AndrewLiu No, it is not the same -- "no longer unambiguous" implies that is used to be unambiguous. Ambiguous just means it is ambiguous now. Both can be used as the reason to specify the tables, but the former implies that without the additional joins the tables specification is not needed. As for siannopollo, he corrected he statement incorrectly and changed the meaning to the opposite of what he wanted. – Gerard ONeill Dec 21 '17 at 23:02
  • Great answer - increased by deletes by 1000x vs in(). However, in 2020, is WHERE statement listed here still needed? What is the point of it? – FlyingZebra1 Dec 29 '20 at 07:27
120

Since you are selecting multiple tables, The table to delete from is no longer unambiguous. You need to select:

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

In this case, table_name1 and table_name2 are the same table, so this will work:

DELETE projects FROM posts INNER JOIN [...]

You can even delete from both tables if you wanted to:

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

Note that order by and limit don't work for multi-table deletes.

Also be aware that if you declare an alias for a table, you must use the alias when referring to the table:

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

Contributions from Carpetsmoker and etc.

George
  • 2,860
  • 18
  • 31
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • 5
    @Yehosef, There's a group of people who find the CAPS really glaring. I believe I'm not the only one, I've seen quite a few people going lowercase style too. – Pacerier Apr 13 '15 at 12:09
  • 1
    fair enough - I respect your right to write your answer in the style that you like ;) – Yehosef Apr 13 '15 at 14:02
  • 1
    You really don't like capitalized keywords, don't you? :D I just saw you writing ` `, when other people would write ` `^^ But your answer still helped me, so take my upvote :) – Cagatay Ulubay Apr 04 '16 at 12:59
  • 10
    To add to the caps/no caps discussion, true, you can use whichever style you like, but in your answer, you're actually mixing styles where you find convenient - the `ON` is capitalized. To less-experienced developers, it may convey that it's OK to be messy and inconsistent in terms of style. – Shade Jul 04 '16 at 15:08
  • @Shade, Good catch, the `ON` is a typo mistake. I'd always lower-case all keywords. Answer edited. – Pacerier Mar 20 '17 at 20:52
  • 28
    CAPS KEYWORDS are not glaring. they ARE MAKING the QUERIES READABLE ;) – Sachem Nov 23 '17 at 21:18
  • @ThinkCode it's a pdo placeholder, you can change `:client_id` to `13` or something else – syahid246 Aug 02 '22 at 22:37
56

You can also use ALIAS like this it works just used it on my database! t is the table need deleting from!

DELETE t FROM posts t
INNER JOIN projects p ON t.project_id = p.project_id
AND t.client_id = p.client_id
Property Spain
  • 569
  • 4
  • 2
  • 1
    this is useful on compound key joins to avoid repeating the table names – Marquez Jun 23 '14 at 19:25
  • 1
    "Actually you can use an alias for joined tables, but not for the main table (posts). 'DELETE posts FROM posts INNER JOIN projects p ON p.project_id = posts.project_id'" —@Weboide – Jeaf Gilbert Oct 23 '14 at 09:54
  • 3
    Actually (quoting from http://dev.mysql.com/doc/refman/5.0/en/delete.html) "If you declare an alias for a table, you must use the alias when referring to the table: DELETE t1 FROM test AS t1, test2 WHERE ..." so using an alias is fine. – Peter Bowers Feb 25 '15 at 07:26
53

Or the same thing, with a slightly different (IMO friendlier) syntax:

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

BTW, with mysql using joins is almost always a way faster than subqueries...

ivanhoe
  • 4,593
  • 1
  • 23
  • 22
  • What does the USING mean? – CMCDragonkai Mar 30 '15 at 00:59
  • 1
    Good explanation of `USING`: http://stackoverflow.com/questions/11366006/mysql-on-vs-using – bigtex777 Apr 08 '15 at 18:08
  • 14
    @bigtex777: Please note that keyword USING in SELECT statements has little to do with the same keyword in a DELETE statement. In SELECTs it specifies the list of columns to join on, while in DELETEs it's a list of all tables in a join – ivanhoe Apr 16 '15 at 01:29
30

I'm more used to the subquery solution to this, but I have not tried it in MySQL:

DELETE  FROM posts
WHERE   project_id IN (
            SELECT  project_id
            FROM    projects
            WHERE   client_id = :client_id
        );
yukondude
  • 24,013
  • 13
  • 49
  • 58
  • 90
    You should really avoid using the IN keyword in SQL (even though it's usually easier to understand for beginners) and use JOIN instead (when possible), as subqueries usually make things a lot slower. – user276648 Oct 28 '11 at 02:05
  • 14
    This tends to crash the DB when there are a huge number of rows returned by the sub query. It is also immensely slow. – Raj Feb 23 '12 at 15:38
  • @user276648 and Raj Sekharan are correct: this is a slow way to do it. But I distinctly recall running up against one RDBMS engine that refused to perform a DELETE containing a join. I wish I could remember which. In any case, I find the IN easier to understand at first glance, and unless there's a quantifiable performance issue, I'd leave it as-is. – yukondude May 23 '12 at 15:36
  • 3
    @yukondude Indeed "IN" is a lot easier to understand than "JOIN" at 1st, and that's why people who are not really familiar with SQL will end up writing "IN" everywhere, while they could use "JOIN" which performs better (or a whooole lot better, depending on the query). I remember several years ago, almost all my SQL queries would be rewritten by someone who actually knew how to write good queries. That's why I added the comment to avoid "IN", so that people would know that if possible, they should avoid using it. – user276648 May 24 '12 at 09:58
  • 1
    @user276648 I think we're basically agreed, especially when it comes to statements other than DELETE. Using IN with a subquery in a SELECT statement, for example, would make it harder to understand the logic to my mind. But for DELETE, I'm willing to let IN slide if there's no measurable detriment in doing so. – yukondude May 24 '12 at 14:57
  • On a query with thousands or millions of records, this sub-select will be poorly performing. The two table join syntax is just as easy as the IN statement. My advice would be to never use IN unless you've got a tiny non-dynamic set of values in the list. – Jim Clouse Sep 12 '12 at 16:23
  • 12
    The very reason I came to this page is because the query I wrote with an IN statement was dog slow. Definitely avoid the accepted answer here. – MikeKulls Jun 14 '13 at 02:15
  • 1
    The other problem with this method is you wouldn't be able to include the posts table in the sub-query...it throws a SQL error – SomethingOn Aug 14 '13 at 14:39
  • 1
    Well, for tasks that are not performance critical, this is a nice, readable solution too! – markus Dec 04 '13 at 11:01
  • 7
    As old as this question is, it is important to know WHY you should use JOIN instead of IN. When that where condition runs on a row, it is going to run that query inside IN. That means, if there are 100 rows that needs to be checked against that WHERE, that subquery is going to be run 100 times. Whereas a JOIN will only run ONCE. So, as your db gets bigger and bigger, that query is going to take longer and longer to finish. @markus just because something is not critical does not mean you should write bad code. Writing it a little better will save you a lot of time and headache in the future. :) – RisingSun Mar 07 '15 at 00:29
  • It's possible only for small data – frops Sep 23 '15 at 12:56
  • SQL Databases have a query optimizer. So how you think the database executes a query is often wrong. Does anbody really *knows* that this slower in current MYSQL? There are DBMS, that internally just convert one form into when they have the same semantic. Also, the delete operiation is typicall slower anyway. And, no, @kudherm, the dbms does not need to check every post against all project-ids. When you have an index on the project-ids, it can just use this index to find all the posts. – Meier Nov 24 '15 at 23:16
  • IN() is basically an OR statement, so for each value that you are trying to delete, mysql will scan the table once, which adds up quick. In my case, I was trying to purge 2M * in 8M* table. The IN() query was 1000 times slower (100,000%) than the inner join query. For delete statement. – FlyingZebra1 Dec 29 '20 at 07:35
17

Single Table Delete:

In order to delete entries from posts table:

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;

In order to delete entries from projects table:

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;

In order to delete entries from clients table:

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;

Multiple Tables Delete:

In order to delete entries from multiple tables out of the joined results you need to specify the table names after DELETE as comma separated list:

Suppose you want to delete entries from all the three tables (posts,projects,clients) for a particular client :

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
1000111
  • 13,169
  • 2
  • 28
  • 37
12

MySQL DELETE records with JOIN

You generally use INNER JOIN in the SELECT statement to select records from a table that have corresponding records in other tables. We can also use the INNER JOIN clause with the DELETE statement to delete records from a table and also the corresponding records in other tables e.g., to delete records from both T1 and T2 tables that meet a particular condition, you use the following statement:

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

Notice that you put table names T1 and T2 between DELETE and FROM. If you omit the T1 table, the DELETE statement only deletes records in the T2 table, and if you omit the T2 table, only records in the T1 table are deleted.

The join condition T1.key = T2.key specifies the corresponding records in the T2 table that need be deleted.

The condition in the WHERE clause specifies which records in the T1 and T2 that need to be deleted.

Aman Garg
  • 3,122
  • 4
  • 24
  • 32
7

Try like below:

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

Another method of deleting using a sub select that is better than using IN would be WHERE EXISTS

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

One reason to use this instead of the join is that a DELETE with JOIN forbids the use of LIMIT. If you wish to delete in blocks so as not to produce full table locks, you can add LIMIT use this DELETE WHERE EXISTS method.

Jim Clouse
  • 8,774
  • 6
  • 32
  • 25
  • 1
    Can this query be written with "aliases"? It isn't very clear from the syntax how the `posts` within EXISTS() is the same `posts` that the rows are deleted from. (IMHO anyway) – MattBianco Oct 03 '14 at 14:18
  • I hear you, but aliases aren't allowed in the table to delete from. The "posts" in the sub query has to be the full table name, which means that if you wanted to re-use that table in your sub-select From clause, you'd have to alias it there. – Jim Clouse Oct 03 '14 at 15:12
  • 1
    This works: `DELETE p FROM posts p WHERE EXISTS ( SELECT 1 FROM projects WHERE projects.client_id = p.client_id);` – MattBianco Oct 06 '14 at 06:40
  • In my case, exists/in() query was ~1000+ times slower. So sure, you can use limit with it, BUT, its pointless, if it takes you 1000 times longer to execute. – FlyingZebra1 Dec 29 '20 at 07:31
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);

DELETE records FROM one table :

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

DELETE RECORDS FROM both tables:

mysql> DELETE tb2,tb1 FROM tb2 JOIN tb1 USING(id);
zloctb
  • 10,592
  • 8
  • 70
  • 89
1

If join does not work for you you may try this solution. It is for deleting orphan records from t1 when not using foreign keys + specific where condition. I.e. it deletes records from table1, that have empty field "code" and that do not have records in table2, matching by field "name".

delete table1 from table1 t1 
    where  t1.code = '' 
    and 0=(select count(t2.name) from table2 t2 where t2.name=t1.name);
Kristjan Adojaan
  • 527
  • 7
  • 10
1

One solution is to use subquery

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 
);

The subquery returns the ID that need to be deleted; all three tables are connected using joins and only those records are deleted that meets the filter condition (in yours case i.e. client_id in the where clause).

0

Try this,

DELETE posts.*
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id
Silambarasan
  • 767
  • 5
  • 19
-3

-- Note that you can not use an alias over the table where you need delete

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
neubert
  • 15,947
  • 24
  • 120
  • 212