How to delete from select in MySQL?
Asked Answered
G

4

110

This code doesn't work for MySQL 5.0, how to re-write it to make it work

DELETE FROM posts where id=(SELECT id FROM posts GROUP BY id  HAVING ( COUNT(id) > 1 ))

I want to delete columns that dont have unique id. I will add that most of the time its only one id(I tried the in syntax and it doesnt work as well).

Gudrunguelderrose answered 30/12, 2010 at 13:1 Comment(0)
D
262

SELECT (sub)queries return result sets. So you need to use IN, not = in your WHERE clause.

Additionally, as shown in this answer you cannot modify the same table from a subquery within the same query. However, you can either SELECT then DELETE in separate queries, or nest another subquery and alias the inner subquery result (looks rather hacky, though):

DELETE FROM posts WHERE id IN (
    SELECT * FROM (
        SELECT id FROM posts GROUP BY id HAVING ( COUNT(id) > 1 )
    ) AS p
)

Or use joins as suggested by Mchl.

Digiovanni answered 30/12, 2010 at 13:2 Comment(12)
I had a table with 150 duplicate keys. I executed the above query and it said "144 rows affected", but there where still duplicate keys. So I executed the query again and it says 5 rows affected, again: 1 row affected. Then all the duplicate keys where gone. Why is this?Pronto
This is happening, because your are only deleting 1 entry out of each set of duplicates: SELECT id FROM posts GROUP BY id HAVING ( COUNT(id) > 1 )Georgetown
#1248 - Every derived table must have its own aliasHighwrought
@thang: Which is why I said to alias the inner subquery.Digiovanni
No, still doesn't work. If you don't alias the inner subquery, it gives you a different error message. I ended up creating a temporary table, dumping everything to it, and then delete where id in that temp table.Highwrought
@thang: Post a separate question. As it is there is nothing I can do if all you're going to give me is an error message and "doesn't work".Digiovanni
@thang: I tried it four years ago and just tried it again now, and it works.Digiovanni
I did the exact same thing. Did you use innodb? I wonder if it depends on the engine.Highwrought
@thang: I tested it on both MyISAM and InnoDB and it works. What is your version of MySQL? I highly recommend posting a separate question so you can provide more details.Digiovanni
let me dig into it some more. probably something stupid i am doing. if it still doesn't work, i'll post a new question. thanks!Highwrought
Will you please explain what does the "As p" does?Rivarivage
@Samarth it is an alias for the derived table (SELECT id FROM posts GROUP BY id HAVING ( COUNT(id) > 1 ))Centeno
F
28
DELETE 
  p1
  FROM posts AS p1 
CROSS JOIN (
  SELECT ID FROM posts GROUP BY id HAVING COUNT(id) > 1
) AS p2
USING (id)
Foreland answered 30/12, 2010 at 13:12 Comment(5)
This appears to work, but I'm confused by the syntax and can't find any resources elsewhere to explain it. CROSS JOIN apparently performs a cartesian join, so seems like this might do unnecessary work, or perform sub-optimally? Could anyone explain?Tetracaine
It will do a cartesian product only if there's no USING clause. With USING the product is limited to pairs having same value in id column, so it is in fact very limited.Foreland
Could you do the same thing with inner join? I.E. DELETE p1 FROM posts AS p1 INNER JOIN ( SELECT ID FROM posts GROUP BY id HAVING COUNT(id) > 1 ) AS p2 ON p2.ID=p1.IDCarisa
@Andrew: Yes. Functionally these joins are exactly the same.Foreland
@Tetracaine CROSS JOIN is the same as [INNER] JOIN in MySQL; it doesn't implement CROSS JOIN in the way that ANSI SQL describes it. SELECT ... FROM a JOIN b USING (x) is syntactic sugar for SELECT ... FROM a JOIN b ON b.x = a.x.Rounding
L
15

you can use inner join :

DELETE 
    ps 
FROM 
    posts ps INNER JOIN 
         (SELECT 
           distinct id 
         FROM 
             posts 
         GROUP BY id  
      HAVING COUNT(id) > 1 ) dubids on dubids.id = ps.id  
Lynch answered 31/3, 2017 at 16:43 Comment(3)
This is great because it works for queries with a composite PKShirleneshirley
When I tested in MySQL 5.7, this is more faster than BoltClock's answer.Hydrogenolysis
This should be THE accepted answer, as it is not using subquery and in general is faster. Way faster.Matriarch
G
0

If you want to delete all duplicates, but one out of each set of duplicates, this is one solution:

DELETE posts
FROM posts
LEFT JOIN (
    SELECT id
    FROM posts
    GROUP BY id
    HAVING COUNT(id) = 1

    UNION

    SELECT id
    FROM posts
    GROUP BY id
    HAVING COUNT(id) != 1
) AS duplicate USING (id)
WHERE duplicate.id IS NULL;
Georgetown answered 10/10, 2013 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.