MySQL DELETE FROM with subquery as condition

162,699

Solution 1

You cannot specify target table for delete.

A workaround

create table term_hierarchy_backup (tid int(10)); <- check data type

insert into term_hierarchy_backup 
SELECT DISTINCT(th1.tid)
FROM term_hierarchy AS th1
INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th1.parent = 1015;

DELETE FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN (select tid from term_hierarchy_backup);

Solution 2

For others that find this question looking to delete while using a subquery, I leave you this example for outsmarting MySQL (even if some people seem to think it cannot be done):

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM tableE
             WHERE arg = 1 AND foo = 'bar');

will give you an error:

ERROR 1093 (HY000): You can't specify target table 'e' for update in FROM clause

However this query:

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM (SELECT id
                   FROM tableE
                   WHERE arg = 1 AND foo = 'bar') x);

will work just fine:

Query OK, 1 row affected (3.91 sec)

Wrap your subquery up in an additional subquery (here named x) and MySQL will happily do what you ask.

Solution 3

The alias should be included after the DELETE keyword:

DELETE th
FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN 
(
    SELECT DISTINCT(th1.tid)
    FROM term_hierarchy AS th1
    INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
    WHERE th1.parent = 1015
);

Solution 4

You need to refer to the alias again in the delete statement, like:

DELETE th FROM term_hierarchy AS th
....

As outlined here in MySQL docs.

Solution 5

I approached this in a slightly different way and it worked for me;

I needed to remove secure_links from my table that referenced the conditions table where there were no longer any condition rows left. A housekeeping script basically. This gave me the error - You cannot specify target table for delete.

So looking here for inspiration I came up with the below query and it works just fine. This is because it creates a temporary table sl1 that is used as the reference for the DELETE.

DELETE FROM `secure_links` WHERE `secure_links`.`link_id` IN 
            (
            SELECT
                `sl1`.`link_id` 
            FROM 
                (
                SELECT 

                    `sl2`.`link_id` 

                FROM 
                    `secure_links` AS `sl2` 
                    LEFT JOIN `conditions` ON `conditions`.`job` = `sl2`.`job` 

                WHERE 

                    `sl2`.`action` = 'something' AND 
                    `conditions`.`ref` IS NULL 
                ) AS `sl1`
            )

Works for me.

Share:
162,699

Related videos on Youtube

mikl
Author by

mikl

I am a web developer, open source enthusiast and a bit of a geek. I work for Docu Media, building web sites with Drupal and creating web applications with Node.js, Ember.js and the like. You can also check out my blog.

Updated on October 11, 2020

Comments

  • mikl
    mikl over 3 years

    I am trying to do a query like this:

    DELETE FROM term_hierarchy AS th
    WHERE th.parent = 1015 AND th.tid IN (
        SELECT DISTINCT(th1.tid)
        FROM term_hierarchy AS th1
        INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
        WHERE th1.parent = 1015
    );
    

    As you can probably tell, I want to delete the parent relation to 1015 if the same tid has other parents. However, that yields me a syntax error:

    You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS th
    WHERE th.parent = 1015 AND th.tid IN (
      SELECT DISTINCT(th1.tid)
      FROM ter' at line 1
    

    I have checked the documentation, and run the subquery by itself, and it all seems to check out. Can anyone figure out what's wrong here?

    Update: As answered below, MySQL does not allow the table you're deleting from be used in a subquery for the condition.

  • ajreal
    ajreal over 13 years
    is not about alias, please check the OP again
  • JNK
    JNK over 13 years
    @ajreal - I did, and please notice the error begins at the alias definition, and MySQL documentation explicitly states you need to use the alias in the DELETE statement as well as the FROM clause. Thanks for the downvote, though.
  • ajreal
    ajreal over 13 years
    simply do this delete from your_table as t1 where t1.id in(select t2.id from your_table t2); what did you get ?
  • Björn
    Björn over 13 years
    The documentation clearly states; Currently, you cannot delete from a table and select from the same table in a subquery. dev.mysql.com/doc/refman/5.5/en/delete.html
  • mikl
    mikl over 13 years
    Ah, as Björn says, it is impossible :( Fixing the alias thing just gives a different error: "You can't specify target table 'th' for update in FROM clause"
  • JNK
    JNK over 13 years
    @mikl - Then you had two errors :) Fix the alias and use ajreal's workaround.
  • JNK
    JNK over 13 years
    we are both right - see his comment to my answer below. Alias syntax and logic were both issues :)
  • mikl
    mikl over 13 years
    Yeah, seems deleting via subquery is not currently possible in MySQL – thanks for taking a look at it :)
  • ajreal
    ajreal over 13 years
    you don't have to fix the alias, just don't specify target table for selecting in delete ...this is the real problem
  • malhal
    malhal over 12 years
    doesn't the "DELETE FROM term_hierarchy AS th" in that last line have the same problem? I get a syntax error the same as the OP.
  • Roman Newaza
    Roman Newaza over 11 years
    You should add Index to term_hierarchy_backup.tid.
  • Tilman Hausherr
    Tilman Hausherr about 11 years
    Took some time but I got it to work. Important: 1) The first table must be aliased as shown here with "e", 2) the "x" at the end is not a placeholder, it is the alias for the temp table produced by the subquery "(SELECT id FROM tableE WHERE arg = 1 AND foo = 'bar')".
  • usumoio
    usumoio over 10 years
    This is a good answer. Proper Aliasing will go a long way to solve problems similar to the original post. (like Mine.)
  • donatJ
    donatJ about 10 years
    Why does this work? This changes a lot for me, but moreover, it shouldn't work. It does work, but it shouldn't.
  • Andrew Starlike
    Andrew Starlike about 10 years
    unbelievable. this actually works! but you are not forced to alias the table with e... you can use any alias you want.
  • CodeReaper
    CodeReaper almost 10 years
    @jakabadambalazs My reasoning when coming up with it, was that the subquery starting with "SELECT id" finishes and returns a list of ids and therefore releases the lock of the table you want to delete from.
  • Steve Almond
    Steve Almond over 9 years
    @jakabadambalazs: We can't use the same table (e) in a DELETE and in its sub-SELECT. We can, however use a sub-sub-SELECT to create a temporary table (x), and use that for the sub-SELECT.
  • Sauron
    Sauron over 9 years
    @CodeReaper Correct me if I'm wrong, but do you think subquery failed because you did not put an alias on the first subquery? at line WHERE arg = 1 AND foo = 'bar'); should be WHERE arg = 1 AND foo = 'bar') 'alias letter';
  • txyoji
    txyoji over 7 years
    Steve Almond is correct. It creates a temp table. Run an explain plan on the delete statement and you'll see what I mean. dev.mysql.com/doc/refman/5.7/en/explain-output.html
  • Terry Lin
    Terry Lin about 7 years
    Incredible, it does work in my case.. even I don't know why
  • Smith
    Smith over 5 years
    Correct - it works because the derived table is executed before the parent query - therefore there's no conflict in reading the table
  • alpham8
    alpham8 over 4 years
    I am able to verify that, that it is neither possible in 2019 on MariaDB 10.3.14 or MySQL Community Server 5.7.27
  • jrypkahauer
    jrypkahauer over 3 years
    This is a brilliant solution... I was already starting to think along these lines and it saved me an hour or so trying to come up with it myself. Thank you so much! @donatJ - it is technically correct and indeed SHOULD work because the intermediate query DOES NOT SPECIFY A TABLE FROM WHICH TO SELECT THE VALUE, it just selects the id value from the subquery... therefore the original error no longer applies...
  • jrypkahauer
    jrypkahauer over 3 years
    Duplicate of the one from @CodeReaper above... good call tho... ;)