it-roy-ru.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?

301
CSchulz

Проблема в том, что MySQL, по какой-то неоправданной причине, не позволяет вам писать такие запросы:

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

То есть, если вы используете UPDATEINSERT/DELETE для таблицы, вы не можете ссылаться на эту таблицу во внутреннем запросе (однако вы _/можете ссылаться на поле из этой внешней таблицы ... )


Решение состоит в том, чтобы заменить экземпляр myTable в подзапросе на (SELECT * FROM myTable), например так

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

Это, очевидно, приводит к тому, что необходимые поля неявно копируются во временную таблицу, поэтому это разрешено.

Я нашел это решение здесь . Примечание из этой статьи:

Вы не хотите просто SELECT * FROM table в подзапросе в реальной жизни; Я просто хотел, чтобы примеры были простыми. На самом деле, вам нужно только выбрать столбцы, которые вам нужны, в этом самом внутреннем запросе и добавить хорошее предложение WHERE, чтобы ограничить результаты.

624
BlueRaja - Danny Pflughoeft

Вы можете сделать это в три этапа:

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

В Mysql вы не можете обновить одну таблицу, выполнив подзапрос этой же таблицы.

Вы можете разделить запрос на две части или сделать

 ОБНОВЛЕНИЕ ТАБЛИЦЫ_А КАК 
 INNER JOIN TABLE_A AS B ON A.field1 = B.field1 
 SET field2 =? 
21
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» для временной таблицы.

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

или похожие.

16
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 [email protected];

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

3
Filippo Mazza

Если вы пытаетесь прочитать fieldA из tableA и сохранить его в 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));

Код выше копирует значение из fieldA в fieldB, когда условие-поле удовлетворяет вашему условию. это также работает в ADO (например, доступ)

источник: попробовал сам

3
krish KM

MariaDB поднял это, начиная с 10.3.x (как для DELETE, так и 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 - Успех

0
Lukasz Szozda