web-dev-qa-db-fra.com

Vous ne pouvez pas spécifier de 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é d'exécuter la mise à jour suivante, mais je n'obtiens que 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 trouvé dans la page suivante de mysql http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html , mais cela ne m'aide pas.

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

301
CSchulz

Le problème est que MySQL, pour une raison quelconque, ne vous permet pas d'écrire des requêtes comme celle-ci:

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

Autrement dit, si vous créez une variable UPDATEINSERT/DELETE sur une table, vous ne pouvez pas référencer cette table dans une requête interne (vous _/can pouvez toutefois référencer un champ à partir de cette table externe ... )


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

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

Cela entraîne apparemment la copie implicite des champs nécessaires dans une table temporaire, donc c'est autorisé.

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

Vous ne voulez pas que SELECT * FROM table dans la sous-requête dans la vie réelle; Je voulais juste garder les exemples simples. En réalité, vous ne devez sélectionner que les colonnes dont vous avez besoin dans la requête la plus profonde et ajouter une bonne clause WHERE pour limiter également les résultats.

624

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
52
Michael Pakhantsov

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 
 JOINDRE INTERNE TABLE_A AS B ON A.field1 = B.field1 
 SET champ2 =? 
21
Yuantao

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 attribué un nouveau nom à la colonne 'persID' pour la table temporaire

20
Budda

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 id, code FROM x) t WHERE t.code='AAA'),
    'BBB'
);

ou similaire.

16
DarkSide

L’approche publiée par BlueRaja est lente, je l’ai modifiée en tant que .__ Je l’utilisais pour supprimer les doublons du tableau Au cas où cela aiderait n'importe qui avec de grandes tables Requête originale

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

Cela prend plus de temps:

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

Solution plus rapide

DELETE FROM table where ID NOT IN(
   SELECT x.Id from (SELECT MIN(Id) as Id from table GROUP BY field2) AS t)
12
Ajak6

À titre de référence, vous pouvez également utiliser les variables Mysql pour enregistrer des résultats temporaires, par exemple:

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

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

3
Filippo Mazza

Si vous essayez de lire fieldA à partir de tableA et de l'enregistrer sur fieldB dans la même table, vous voudrez peut-être en tenir compte lorsque fieldc = fieldd.

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

Le code ci-dessus copie la valeur de fieldA à fieldB lorsque condition-field remplissait votre condition. cela fonctionne aussi dans ADO (par exemple, accès)

source: essayé moi-même

3
krish KM

MariaDB a levé ce point à partir de 10.3.x (pour DELETE et UPDATE)

UPDATE - Instructions de même source et cible

A partir de MariaDB 10.3.2, les instructions UPDATE peuvent avoir la même source et la même cible.

Jusqu'à MariaDB 10.3.1, l'instruction UPDATE suivante ne fonctionnerait pas:

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

A partir de MariaDB 10.3.2, l'instruction s'exécute correctement:

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

DELETE - Même source et table cible

Jusqu'à MariaDB 10.3.1, la suppression d'une table avec les mêmes source et cible était impossible. À partir de MariaDB 10.3.1, cela est maintenant possible. Par exemple:

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

DBFiddle MariaDB 10.2 - Erreur

DBFiddle MariaDB 10.3 - Succès

0
Lukasz Szozda

L'utilisation de SELECT DISTINCT ou LIMIT dans la sous-requête est une autre solution de contournement, bien que leur effet sur la matérialisation ne soit pas aussi explicite. cela a fonctionné pour moi

comme mentionné dans MySql Doc

0
PITU