Nie można określić tabeli docelowej dla aktualizacji w klauzuli FROM

Mam prostą tabelę mysql:

CREATE TABLE IF NOT EXISTS `pers` (
  `persID` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(35) NOT NULL,
  `gehalt` int(11) NOT NULL,
  `chefID` int(11) DEFAULT NULL,
  PRIMARY KEY (`persID`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

INSERT INTO `pers` (`persID`, `name`, `gehalt`, `chefID`) VALUES
(1, 'blb', 1000, 3),
(2, 'as', 1000, 3),
(3, 'chef', 1040, NULL);

Próbowałem uruchomić po aktualizacji, ale dostaję tylko błąd 1093:

UPDATE pers P 
SET P.gehalt = P.gehalt * 1.05 
WHERE (P.chefID IS NOT NULL 
OR gehalt < 
(SELECT (
    SELECT MAX(gehalt * 1.05) 
    FROM pers MA 
    WHERE MA.chefID = MA.chefID) 
    AS _pers
))

Wyszukałem błąd i znalazłem z mysql po stronie http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html , ale to mi nie pomaga.

Co zrobić, aby poprawić zapytanie sql?

Author: OMG Ponies, 2010-12-13

11 answers

Problem polega na tym, że MySQL, z jakiegokolwiek powodu, nie pozwala na pisanie zapytań takich jak:

UPDATE myTable
SET myTable.A =
(
    SELECT B
    FROM myTable
    INNER JOIN ...
)

To znaczy, jeśli robisz UPDATE/INSERT/DELETE w tabeli nie można odwoływać się do tej tabeli w zapytaniu wewnętrznym (można można jednak odwoływać się do pola z tej tabeli zewnętrznej...)


Rozwiązaniem jest zastąpienie instancji myTable w zapytaniu podrzędnym przez (SELECT * FROM myTable), tak jak to

UPDATE myTable
SET myTable.A =
(
    SELECT B
    FROM (SELECT * FROM myTable) AS something
    INNER JOIN ...
)

To najwyraźniej powoduje, że niezbędne pola są domyślnie skopiowane do tymczasowej tabeli, więc jest to dozwolone.

Znalazłem to rozwiązanie tutaj . Uwaga z tego artykułu:

Nie chcesz po prostu SELECT * FROM table w subquery w prawdziwym życiu; chciałem tylko, aby przykłady były proste. W rzeczywistości powinieneś wybrać tylko te kolumny, których potrzebujesz w tym najbardziej wewnętrznym zapytaniu, i dodać dobrą klauzulę WHERE, aby ograniczyć wyniki.

 807
Author: BlueRaja - Danny Pflughoeft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-16 16:04:01

Możesz to zrobić w trzech krokach:

CREATE TABLE test2 AS
SELECT PersId 
FROM pers p
WHERE (
  chefID IS NOT NULL 
  OR gehalt < (
    SELECT MAX (
      gehalt * 1.05
    )
    FROM pers MA
    WHERE MA.chefID = p.chefID
  )
)

...

UPDATE pers P
SET P.gehalt = P.gehalt * 1.05
WHERE PersId
IN (
  SELECT PersId
  FROM test2
)
DROP TABLE test2;

Lub

UPDATE Pers P, (
  SELECT PersId
  FROM pers p
  WHERE (
   chefID IS NOT NULL 
   OR gehalt < (
     SELECT MAX (
       gehalt * 1.05
     )
     FROM pers MA
     WHERE MA.chefID = p.chefID
   )
 )
) t
SET P.gehalt = P.gehalt * 1.05
WHERE p.PersId = t.PersId
 53
Author: Michael Pakhantsov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-10 16:14:44

W Mysql, nie można zaktualizować jednej tabeli przez subquery tej samej tabeli.

Możesz rozdzielić zapytanie na dwie części, lub zrobić

 UPDATE TABLE_A AS A
 INNER JOIN TABLE_A AS B ON A.field1 = B.field1
 SET field2 = ? 
 27
Author: Yuantao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-31 02:42:20

Utwórz tymczasową tabelę (tempP) z zapytania podrzędnego

UPDATE pers P 
SET P.gehalt = P.gehalt * 1.05 
WHERE P.persID IN (
    SELECT tempP.tempId
    FROM (
        SELECT persID as tempId
        FROM pers P
        WHERE
            P.chefID IS NOT NULL OR gehalt < 
                (SELECT (
                    SELECT MAX(gehalt * 1.05) 
                    FROM pers MA 
                    WHERE MA.chefID = MA.chefID) 
                    AS _pers
                )
    ) AS tempP
)

Wprowadziłem osobną nazwę (alias) i nadałem nową nazwę kolumnie 'persID' dla tymczasowej tabeli

 24
Author: Budda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-09 20:42:56

To dość proste. Na przykład zamiast pisać:

INSERT INTO x (id, parent_id, code) VALUES (
    NULL,
    (SELECT id FROM x WHERE code='AAA'),
    'BBB'
);

Powinieneś napisać

INSERT INTO x (id, parent_id, code)
VALUES (
    NULL,
    (SELECT t.id FROM (SELECT id, code FROM x) t WHERE t.code='AAA'),
    'BBB'
);

Lub podobne.

 18
Author: DarkSide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-26 15:26:20

Podejście dodane przez BlueRaja jest powolne zmodyfikowałem je jako Używałem do usuwania duplikatów z tabeli. W przypadku, gdy pomoże to każdemu z dużymi stołami Oryginalne Zapytanie

delete from table where id not in (select min(id) from table group by field 2)

To zajmuje więcej czasu:

DELETE FROM table where ID NOT IN(
  SELECT MIN(t.Id) from (select Id,field2 from table) AS t GROUP BY field2)

Faster Solution

DELETE FROM table where ID NOT IN(
   SELECT x.Id from (SELECT MIN(Id) as Id from table GROUP BY field2) AS t)
 15
Author: Ajak6,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-07 21:01:12

Jako odniesienie, Możesz również użyć zmiennych Mysql do zapisywania tymczasowych wyników, np.:

SET @v1 := (SELECT ... );
UPDATE ... SET ... WHERE x=@v1;

Https://dev.mysql.com/doc/refman/5.7/en/user-variables.html

 7
Author: Filippo Mazza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-06 08:16:31

MariaDB podniósł to począwszy od 10.3.x (zarówno dla DELETE jak i UPDATE):

UPDATE - instrukcje z tym samym źródłem i celem

Od MariaDB 10.3.2, Instrukcje aktualizacji mogą mieć to samo źródło i cel.

Do MariaDB 10.3.1 następująca instrukcja aktualizacji nie będzie działać:

UPDATE t1 SET c1=c1+1 WHERE c2=(SELECT MAX(c2) FROM t1);
  ERROR 1093 (HY000): Table 't1' is specified twice, 
  both as a target for 'UPDATE' and as a separate source for data

Od MariaDB 10.3.2 polecenie wykonuje się pomyślnie:

UPDATE t1 SET c1=c1+1 WHERE c2=(SELECT MAX(c2) FROM t1);

DELETE-to samo źródło i Tabela Docelowa

Do MariaDB 10.3.1 usunięcie z tabeli z tym samym źródłem i celem nie było możliwe. Od MariaDB 10.3.1 jest to teraz możliwe. Na przykład:

DELETE FROM t1 WHERE c1 IN (SELECT b.c1 FROM t1 b WHERE b.c2=0);

DBFiddle MariaDB 10.2-Error

DBFiddle MariaDB 10.3-Sukces

 4
Author: Lukasz Szozda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 17:40:22

Jeśli próbujesz odczytać fieldA z tableA i zapisać go na fieldB w tej samej tabeli, gdy fieldc = fieldd możesz chcieć to rozważyć.

UPDATE tableA,
    tableA AS tableA_1 
SET 
    tableA.fieldB= tableA_1.filedA
WHERE
    (((tableA.conditionFild) = 'condition')
        AND ((tableA.fieldc) = tableA_1.fieldd));

Powyższy kod kopiuje wartość z fieldA do fieldB, gdy warunek-pole spełnia Twój warunek. działa to również w ADO (np. access)

Źródło: wypróbowałem siebie

 3
Author: Krish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-08 09:37:44

MySQL nie pozwala na wybór z tabeli i aktualizację w tej samej tabeli w tym samym czasie. Ale zawsze jest obejście:)

To nie działa.>>>>

UPDATE table1 SET col1 = (SELECT MAX(col1) from table1) WHERE col1 IS NULL;

Ale to działa >>>>

UPDATE table1 SET col1 = (SELECT MAX(col1) FROM (SELECT * FROM table1) AS table1_new) WHERE col1 IS NULL;
 2
Author: Hari Das,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-15 04:44:52

Inne obejścia obejmują użycie SELECT DISTINCT lub LIMIT w zapytaniu podrzędnym, chociaż nie są one tak wyraźne w ich wpływie na materializację. this worked for me

Jak wspomniano w MySql Doc

 0
Author: PITU,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-26 06:51:00