Table is specified twice, both as a target for 'UPDATE' and as a separate source for data in mysql

Mysql

Mysql Problem Overview


I have below query in mysql where I want to check if branch id and year of finance type from branch_master are equal with branch id and year of manager then update status in manager table against branch id in manager

UPDATE manager as m1 
  SET m1.status = 'Y'
  WHERE m1.branch_id IN (
    SELECT m2.branch_id FROM manager as m2 
     WHERE (m2.branch_id,m2.year) IN (
        (
          SELECT DISTINCT branch_id,year 
            FROM `branch_master` 
           WHERE type = 'finance'
        )
     )
  )

but getting error

> Table 'm1' is specified twice, both as a target for 'UPDATE' and as a > separate source for data

Mysql Solutions


Solution 1 - Mysql

This is a typical MySQL thing and can usually be circumvented by selecting from the table derived, i.e. instead of

FROM manager AS m2

use

FROM (select * from manager) AS m2

The complete statement:

UPDATE manager
SET status = 'Y'
WHERE branch_id IN
(
  select branch_id
  FROM (select * from manager) AS m2
  WHERE (branch_id, year) IN
  (
    SELECT branch_id, year
    FROM branch_master
    WHERE type = 'finance'
  )
);

Solution 2 - Mysql

The correct answer is in this SO post.

The problem with here accepted answer is - as was already mentioned multiple times - creating a full copy of the whole table. This is way far from optimal and the most space complex one. The idea is to materialize the subset of data used for update only, so in your case it would be like this:

UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
    SELECT * FROM(
        SELECT m2.branch_id FROM manager as m2 
        WHERE (m2.branch_id,m2.year) IN (
            SELECT DISTINCT branch_id,year 
            FROM `branch_master` 
            WHERE type = 'finance')
    ) t
)

Basically you just encapsulate your previous source for data query inside of

SELECT * FROM (...) t

Solution 3 - Mysql

Try to use the EXISTS operator:

UPDATE manager as m1 
SET m1.status = 'Y'
WHERE EXISTS (SELECT 1
              FROM (SELECT m2.branch_id             
                    FROM branch_master AS bm
                    JOIN manager AS m2
                    WHERE bm.type = 'finance' AND
						bm.branch_id = m2.branch_id AND
                        bm.year = m2.year) AS t
              WHERE t.branch_id = m1.branch_id); 

Note: The query uses an additional nesting level, as proposed by @Thorsten, as a means to circumvent the Table is specified twice error.

Demo here

Solution 4 - Mysql

Try :::

UPDATE manager as m1 
SET m1.status = 'Y'
WHERE m1.branch_id IN (
  (SELECT DISTINCT branch_id
  FROM branch_master
  WHERE type = 'finance'))
  AND m1.year IN ((SELECT DISTINCT year
  FROM branch_master 
  WHERE type = 'finance'))

Solution 5 - Mysql

The problem I had with the accepted answer is that create a copy of the whole table, and for me wasn't an option, I tried to execute it but after several hours I had to cancel it.

A very fast way if you have a huge amount of data is create a temporary table:

  1. Create TMP table

    CREATE TEMPORARY TABLE tmp_manager (branch_id bigint auto_increment primary key, year datetime null);

  2. Populate TMP table

    insert into tmp_manager (branch_id, year) select branch_id, year from manager;

  3. Update with join

    UPDATE manager as m, tmp_manager as tmp_m inner JOIN manager as man on tmp_m.branch_id = man.branch_id SET status = 'Y' WHERE m.branch_id = tmp_m.branch_id and m.year = tmp_m.year and m.type = 'finance';

Solution 6 - Mysql

This is by far the fastest way:

UPDATE manager m
INNER JOIN branch_master b on m.branch_id=b.branch_id AND m.year=b.year
SET m.status='Y'
WHERE b.type='finance'

Note that if it is a 1:n relationship the SET command will be run more than once. In this case that is no problem. But if you have something like "SET price=price+5" you cannot use this construction.

Solution 7 - Mysql

Maybe not a solution, but some thoughts about why it doesn't work in the first place:

Reading data from a table and also writing data into that same table is somewhat an ill-defined task. In what order should the data be read and written? Should newly written data be considered when reading it back from the same table? MySQL refusing to execute this isn't just because of a limitation, it's because it's not a well-defined task.

The solutions involving SELECT ... FROM (SELECT * FROM table) AS tmp just dump the entire content of a table into a temporary table, which can then be used in any further outer queries, like for example an update query. This forces the order of operations to be: Select everything first into a temporary table and then use that data (instead of the data from the original table) to do the updates.

However if the table involved is large, then this temporary copying is going to be incredibly slow. No indexes will ever speed up SELECT * FROM table.

I might have a slow day today... but isn't the original query identical to this one, which souldn't have any problems?

UPDATE manager as m1 
SET m1.status = 'Y'
WHERE (m1.branch_id, m1.year) IN (
      SELECT DISTINCT branch_id,year 
        FROM `branch_master` 
       WHERE type = 'finance'
    )

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhrishiView Question on Stackoverflow
Solution 1 - MysqlThorsten KettnerView Answer on Stackoverflow
Solution 2 - MysqlshadyyxView Answer on Stackoverflow
Solution 3 - MysqlGiorgos BetsosView Answer on Stackoverflow
Solution 4 - MysqlPrinceKayasthaView Answer on Stackoverflow
Solution 5 - MysqlDamian LatteneroView Answer on Stackoverflow
Solution 6 - Mysqluser2587656View Answer on Stackoverflow
Solution 7 - MysqljlhView Answer on Stackoverflow