【问题标题】:You can't specify target table for update in FROM clause您不能在 FROM 子句中指定要更新的目标表
【发布时间】:2011-05-24 16:18:55
【问题描述】:

我有一个简单的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查询?

【问题讨论】:

标签: sql mysql mysql-error-1093


【解决方案1】:

您可以分三步完成:

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

【讨论】:

  • 嗯,是的,大多数子查询可以用CREATE TABLE 语句重写为多个步骤——我希望作者意识到这一点。然而,这是唯一的解决方案吗?或者可以用子查询或连接重写查询吗?为什么(不)这样做?
  • 我认为您在第二个解决方案中存在大写错误。 UPDATE Pers P不应该读成UPDATE pers P吗?
  • 尝试了这个解决方案,对于临时/第二表中的大量条目,查询可能非常慢;尝试使用索引/主键创建临时/第二个表 [参见dev.mysql.com/doc/refman/5.1/en/create-table-select.html]
  • 正如@Konerak 所说,这并不是最好的答案。下面 BlueRaja 的答案对我来说似乎是最好的。赞成票似乎同意。
  • @Konerak,CREATE TABLE AS SELECT 的表现不是很糟糕吗?
【解决方案2】:

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

您可以将查询分成两部分,或者这样做

将 TABLE_A 更新为 A INNER JOIN TABLE_A AS B ON A.field1 = B.field1 设置字段2 = ?

【讨论】:

  • SELECT ... SET?我从来没有听说过这个。
  • @grisson 感谢您的澄清。现在我明白了为什么我的 IN 子句不起作用 - 我的目标是同一张表。
  • ...这似乎并不实际。它仍然给我同样的错误。
  • 这个答案实际上做了更正确和更有效的事情,即在第二次引用AS B时使用TABLE_A。可以使用AS T 而不是可能效率低下的FROM (SELECT * FROM myTable) AS something 来简化最受支持的示例中的答案,幸运的是查询优化器通常会消除但可能并不总是这样做。
【解决方案3】:

从子查询中创建一个临时表 (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”列提供了一个新名称

【讨论】:

  • 为什么不将值选择到变量中而不是进行内部内部内部选择?
  • SELECT ( SELECT MAX(gehalt * 1.05).. - 第一个 SELECT 不选择任何列。
【解决方案4】:

这很简单。例如,不要写:

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'
);

或类似的。

【讨论】:

    【解决方案5】:

    问题在于 MySQL,无论出于何种原因,都不允许您编写这样的查询:

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

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


    解决方法是将子查询中myTable的实例替换为(SELECT * FROM myTable),像这样

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

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

    我找到了这个解决方案here。那篇文章的注释:

    您不想在现实生活中的子查询中只使用SELECT * FROM table;我只是想让示例保持简单。实际上,您应该只在最里面的查询中选择您需要的列,并添加一个好的WHERE 子句来限制结果。

    【讨论】:

    • 我不认为原因是空洞的。考虑语义。 MySQL 必须在更新开始之前保留表的副本,或者内部查询可能会使用已被查询更新的数据,因为它正在进行中。这些副作用都不一定是可取的,因此最安全的选择是强制您使用额外的表格指定会发生什么。
    • @siride:其他数据库,如MSSQL或Oracle,没有这个任意限制
    • @BlueRaja-DannyPflughoeft:这不是任意的。这是基于备选方案成本的合理设计决策。无论如何,其他数据库系统选择处理这些成本。但是这些系统不允许,例如,当您使用 GROUP BY 时,让您在 SELECT 列表中包含非聚合列,而 MySQL 可以。我认为 MySQL 在这里是错误的,我可能会对 UPDATE 语句的其他 DBMS 说同样的话。
    • @siride 从关系代数的角度来看,T(SELECT * FROM T) 是完全等价的。它们是相同的关系。因此,这是一个任意的、毫无意义的限制。更具体地说,这是一种强制 MySQL 做一些它显然可以做的事情,但由于某种原因它不能以更简单的形式解析的解决方法。
    • 在我的情况下,接受的解决方案不起作用,因为我的表太大了。查询从未完成。显然这占用了太多的内部资源。相反,我使用内部查询创建了一个视图,并将其用于数据选择,效果非常好。 DELETE FROM t WHERE tableID NOT IN (SELECT viewID FROM t_view); 另外我建议之后运行OPTIMIZE TABLE t; 以减小表格的大小。
    【解决方案6】:

    如果您尝试从 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));
    

    当条件字段满足您的条件时,上面的代码将值从字段A复制到字段B。这也适用于 ADO(例如 access )

    来源:我自己试过

    【讨论】:

      【解决方案7】:

      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 t.Id FROM (SELECT MIN(Id) AS Id FROM table GROUP BY field2) AS t)
      

      【讨论】:

        【解决方案8】:

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

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

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

        【讨论】:

        • 一般来说这很好,但它不适用于更新/删除多行ERROR 1242 (21000): Subquery returns more than 1 row
        【解决方案9】:

        MariaDB 从 10.3.x 开始取消了这个功能(DELETEUPDATE):

        UPDATE - Statements With the Same Source and Target

        从 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 - Same Source and Target Table

        在 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 - Error

        DBFiddle MariaDB 10.3 - Success

        【讨论】:

          【解决方案10】:

          其他解决方法包括在子查询中使用 SELECT DISTINCT 或 LIMIT,尽管它们对具体化的影响并不那么明确。这对我有用

          as mentioned in MySql Doc

          【讨论】:

            【解决方案11】:

            MySQL 不允许从一个表中选择并同时在同一个表中更新。但总有一种解决方法:)

            这不起作用>>>>

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

            但这行得通>>>>

            UPDATE table1 SET col1 = (SELECT MAX(col1) FROM (SELECT * FROM table1) AS table1_new) WHERE col1 IS NULL;
            

            【讨论】:

            • 谢谢。我认为大多数人对使用 MariaDB 的本地环境 xampp 感到困惑,然后生产服务器仍然使用 MySQL
            猜你喜欢
            • 2015-07-10
            • 2014-07-31
            • 2015-09-15
            • 2012-07-29
            • 2017-01-12
            相关资源
            最近更新 更多