437 votes

Vous ne pouvez pas spécifier la table cible pour la mise à jour dans la clause FROM

J'ai une simple table 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);
 

J'ai essayé de courir après la mise à jour, mais je reçois seulement l'erreur 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
))
 

J'ai cherché l'erreur et j'ai trouvé mysql page suivante http://dev.mysql.com/doc/refman/5.1/fr/subquery-restrictions.html , mais cela ne m'aide pas.

Que dois-je faire pour corriger la requête SQL?

877voto

Car personne d'autre ne l'a posté...

Le problème est que MySQL, pour une stupide raison, il ne vous permet pas d'écrire des requêtes comme ceci:

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

C'est, si vous êtes en train de faire un UPDATE/INSERT/DELETE sur une table, vous ne pouvez pas référencer la table dans une requête interne (vous pouvez toutefois faire référence à un champ à partir de cette table externe...)


La solution est de remplacer l'instance de myTable dans la sous-requête avec (SELECT * FROM myTable), comme ceci

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

Ce qui, apparemment, les causes les champs nécessaires pour être implicitement copié dans une table temporaire, donc c'est permis.

J'ai trouvé cette solution ici. Une note à cet article:

Vous ne voulez pas juste SELECT * FROM table dans la sous-requête dans la vraie vie; je voulais juste garder les exemples simples. En réalité, vous devez uniquement sélectionner les colonnes que vous avez besoin dans ce plus intime de la requête, et l'ajout d'un bon WHERE clause de limiter les résultats, trop.

53voto

Michael Pakhantsov Points 11344

Vous pouvez le faire en trois étapes:

 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;
 

ou

 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
 

29voto

grisson Points 547

Dans Mysql, vous ne pouvez pas mettre à jour une table en sous-interrogeant la même table.

Vous pouvez séparer la requête en deux parties, ou faire

 UPDATE TABLE_A AS A
 INNER JOIN TABLE_A COMME B ON A.field1 = B.field1
 SET champ2 =? 

26voto

Budda Points 5575

Créer une table temporaire (tempP) à partir d'une sous-requête

 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
)
 

J'ai introduit un nom distinct (alias) et donne un nouveau nom à la colonne 'persID' pour la table temporaire

19voto

DarkSide Points 1873

C'est assez simple. Par exemple, au lieu d'écrire:

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

tu devrais écrire

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

ou similaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X