关于sql:您无法在FROM子句中指定要更新的目标表

You can't specify target table for update in FROM clause

我有一个简单的mysql表:

1
2
3
4
5
6
7
8
9
10
11
12
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:

1
2
3
4
5
6
7
8
9
10
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
))

我搜索了错误,并在http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html页面的mysql中找到,但它对我没有帮助。

我该怎么做才能纠正sql查询?


问题在于,无论出于什么原因,MySQL都不允许你编写这样的查询:

1
2
3
4
5
6
7
UPDATE myTable
SET myTable.A =
(
    SELECT B
    FROM myTable
    INNER JOIN ...
)

也就是说,如果您在表上执行UPDATE / INSERT / DELETE,则无法在内部查询中引用该表(但您可以从该外部表中引用该字段...)

解决方案是用(SELECT * FROM myTable)替换子查询中的myTable实例,就像这样

1
2
3
4
5
6
7
UPDATE myTable
SET myTable.A =
(
    SELECT B
    FROM (SELECT * FROM myTable) AS something
    INNER JOIN ...
)

这显然会导致必要的字段被隐式复制到临时表中,所以它是允许的。

我在这里找到了解决方案。该文的说明:

You don’t want to just SELECT * FROM table in the subquery in real life; I just wanted to keep the examples simple. In reality, you should only be selecting the columns you need in that innermost query, and adding a good WHERE clause to limit the results, too.


您可以分三步完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
  )
)

...

1
2
3
4
5
6
7
8
UPDATE pers P
SET P.gehalt=P.gehalt * 1.05
WHERE PersId
IN (
  SELECT PersId
  FROM test2
)
DROP TABLE test2;

要么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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


在Mysql中,您不能通过子查询同一个表来更新一个表。

您可以将查询分为两部分,或者执行

1
2
3
 UPDATE TABLE_A AS A
 INNER JOIN TABLE_A AS B ON A.field1 = B.field1
 SET field2 = ?


从子查询创建临时表(tempP)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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'列提供了一个新名称


这很简单。例如,而不是写:

1
2
3
4
5
INSERT INTO x (id, parent_id, code) VALUES (
    NULL,
    (SELECT id FROM x WHERE code='AAA'),
    'BBB'
);

你应该写

1
2
3
4
5
6
INSERT INTO x (id, parent_id, code)
VALUES (
    NULL,
    (SELECT t.id FROM (SELECT id, code FROM x) t WHERE t.code='AAA'),
    'BBB'
);

或类似的。


BlueRaja发布的方法很慢我将其修改为
我用来删除表中的重复项。万一它可以帮助任何人拥有大桌子
原始查询

1
DELETE FROM TABLE WHERE id NOT IN (SELECT MIN(id) FROM TABLE GROUP BY FIELD 2)

这需要更多时间:

1
2
DELETE FROM TABLE WHERE ID NOT IN(
  SELECT MIN(t.Id) FROM (SELECT Id,field2 FROM TABLE) AS t GROUP BY field2)

更快的解决方案

1
2
DELETE FROM TABLE WHERE ID NOT IN(
   SELECT x.Id FROM (SELECT MIN(Id) AS Id FROM TABLE GROUP BY field2) AS t)


作为参考,您也可以使用Mysql变量来保存临时结果,例如:

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

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


如果您尝试从tableA读取fieldA并将其保存在同一个表的fieldB上,则当fieldc = fieldd时,您可能需要考虑这一点。

1
2
3
4
5
6
7
UPDATE tableA,
    tableA AS tableA_1
SET
    tableA.fieldB= tableA_1.filedA
WHERE
    (((tableA.conditionFild) = 'condition')
        AND ((tableA.fieldc) = tableA_1.fieldd));

当条件字段满足您的条件时,上面的代码将fieldA中的值复制到fieldB。这也适用于ADO(例如访问)

来源:试过自己


MariaDB从10.3.x(DELETEUPDATE)开始提升了这一点:

UPDATE - Statements With the Same Source and Target

From MariaDB 10.3.2, UPDATE statements may have the same source and target.

Until MariaDB 10.3.1, the following UPDATE statement would not work:

1
2
3
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

From MariaDB 10.3.2, the statement executes successfully:

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

DELETE - Same Source and Target Table

Until MariaDB 10.3.1, deleting from a table with the same source and target was not possible. From MariaDB 10.3.1, this is now possible. For example:

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 - 成功


update x set available_material_id = null其中id不在(从x中选择id,其中additional_info = 1);

1
2
3
4
5
6
-- use left join ----
UPDATE x LEFT JOIN
       x xx
       ON x.id = xx.id AND xx.additional_info = 1
    SET available_material_id = NULL
    WHERE xx.id IS NULL;