web-dev-qa-db-ja.com

FROM句に更新対象の表は指定できません

私は単純な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);

次のアップデートを実行しようとしましたが、エラー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
))

私はエラーを検索し、mysql次のページから見つけました http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html しかし、それは私を助けません。

SQLクエリを修正するにはどうすればよいですか。

326
CSchulz

問題なのは、MySQLは、なんらかの理由で、このようなクエリを書くことができないことです。

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

つまり、テーブルに対してUPDATE/INSERT/DELETEを実行している場合、内部クエリでそのテーブルを参照することはできません(youcanしかし、その外側のテーブルのフィールドを参照しています...)


この問題を解決するには、サブクエリのmyTableのインスタンスを(SELECT * FROM myTable)に置き換えます。

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

これにより、必要なフィールドが一時テーブルに暗黙的にコピーされるように見えるため、許可されています。

私はこの解決策 をここで見つけました 。その記事からのメモ:

実生活では、副照会の中にSELECT * FROM tableだけを入れたくはありません。例を単純にしておきたかっただけです。実際には、その最も内側のクエリに必要な列を選択し、結果を制限するために適切なWHERE句を追加するだけで済みます。

これは3つのステップで行うことができます。

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;

または

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

Mysqlでは、同じテーブルをサブクエリにして1つのテーブルを更新することはできません。

あなたは2つの部分にクエリを分けることができます

 UPDATE TABLE_A AS A 
内部結合TABLE_A AS B ON A.field1 = B.field1 
 SET field2 =? 
23
Yuantao

副照会から一時表(tempP)を作成します。

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
)

私は別の名前(エイリアス)を導入し、一時テーブルの 'persID'列に新しい名前を付けました

21
Budda

とても簡単です。例えば、書く代わりに、

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

あなたが書くべき

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

または類似。

17
DarkSide

BlueRajaによって投稿されたアプローチは遅く、私はテーブルから重複を削除するために使っていたのでそれを修正しました。それが大きなテーブルを持っている人をだれでも助ける場合オリジナルクエリ

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

これにはもっと時間がかかります。

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

より速い解決

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

参考までに、Mysql変数を使用して一時的な結果を保存することもできます。

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

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

3
Filippo Mazza

TableAからfieldAを読み取り、それを同じテーブルのfieldBに保存しようとしている場合、fieldc = fielddの場合はこれを考慮する必要があります。

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

上記のコードは、condition-fieldが条件に一致したときに、fieldAからfieldBに値をコピーします。これはADOでも動作します(例:アクセス)

ソース:自分で試した

3
krish KM

MariaDBはこれを10.3.xから引き上げました(DELETEUPDATEの両方)。

UPDATE - 同じソースとターゲットを持つステートメント

MariaDB 10.3.2から、UPDATEステートメントは同じソースとターゲットを持つことができます。

MariaDB 10.3.1までは、次のUPDATEステートメントは機能しませんでした。

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

MariaDB 10.3.2から、このステートメントは正常に実行されます。

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

DELETE - 同じソーステーブルとターゲットテーブル

MariaDB 10.3.1までは、同じソースとターゲットを持つテーブルから削除することはできませんでした。 MariaDB 10.3.1から、これは可能になりました。例えば:

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

DBFiddle MariaDB 10.2 - エラー

DBFiddle MariaDB 10.3 - 成功

1
Lukasz Szozda

その他の回避策には、サブクエリでのSELECT DISTINCTまたはLIMITの使用が含まれますが、これらは具体化への影響がそれほど明確ではありません。これは私のために働いた

MySql Docで述べたように

0
PITU