FROM 절에서 업데이트 대상 테이블을 지정할 수 없습니다


379

간단한 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 쿼리를 수정하려면 어떻게해야합니까?


답변:


769

문제는 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 ...
)

이로 인해 필요한 필드가 임시 테이블에 내재적으로 복사되므로 허용됩니다.

이 솔루션을 여기 에서 찾았 습니다 . 그 기사의 메모 :

SELECT * FROM table실제 쿼리에서는 하위 쿼리 만 수행하고 싶지 않습니다 . 예제를 단순하게 유지하고 싶었습니다. 실제로 가장 안쪽 쿼리에 필요한 열만 선택 WHERE하고 결과를 제한하기 위해 좋은 절을 추가해야합니다 .


10
나는 그 이유가 미쳤다고 생각하지 않습니다. 의미에 대해 생각하십시오. MySQL은 업데이트가 시작되기 전에 테이블의 복사본을 유지해야합니다. 그렇지 않으면 내부 쿼리가 쿼리가 진행중인 쿼리에 의해 이미 업데이트 된 데이터를 사용할 수 있습니다. 이러한 부작용 중 어느 것도 반드시 바람직하지는 않으므로 가장 안전한 방법은 추가 테이블을 사용하여 발생할 일을 지정하도록하는 것입니다.
siride

35
@siride : 같은 MSSQL이나 오라클과 같은 다른 데이터베이스는이 임의의 제한이 없습니다
대니 Pflughoeft - BlueRaja

3
@ BlueRaja-DannyPflughoeft : 그것은 임의적이지 않습니다. 대안 비용을 기준으로 합리적인 설계 결정을 내립니다. 다른 DB 시스템은 이러한 비용을 처리하기로 결정했습니다. 그러나 이러한 시스템은 예를 들어 GROUP BY를 사용할 때 SELECT 목록에 집계되지 않은 열을 포함시킬 수 없으며 MySQL은 그렇지 않습니다. MySQL이 잘못되었다고 주장하고 UPDATE 문에 대해 다른 DBMS와 동일하게 말할 수 있습니다.
siride

33
보기의 관계 대수의 관점에서 @siride, T(SELECT * FROM T)완전히 동일합니다. 그들은 같은 관계입니다. 따라서 이것은 임의의 제각기 제한입니다. 보다 구체적으로, MySQL이 분명하게 할 수있는 일을하도록 강요하는 방법이지만, 어떤 이유로 더 간단한 형태로 구문 분석 할 수 없습니다.
Tobia

4
제 경우에는 테이블이 너무 커서 허용 된 솔루션이 작동하지 않았습니다. 쿼리가 완료되지 않았습니다. 분명히 이것은 너무 많은 내부 리소스를 사용하고 있습니다. 대신 내부 쿼리로보기를 만들고 데이터 선택에 사용했는데 절대적으로 잘 작동했습니다. DELETE FROM t WHERE tableID NOT IN (SELECT viewID FROM t_view);또한 OPTIMIZE TABLE t;테이블 크기를 줄이려면 나중에 실행하는 것이 좋습니다 .
CodeX

53

세 단계로이 작업을 수행 할 수 있습니다.

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

16
글쎄, 대부분의 하위 쿼리는 CREATE TABLE문장 으로 여러 단계로 다시 작성할 수 있습니다 . 저자가 이것을 알고 있었기를 바랍니다. 그러나 이것이 유일한 해결책입니까? 아니면 하위 쿼리 나 조인으로 쿼리를 다시 작성할 수 있습니까? 왜 그렇게하지 않습니까?
Konerak

두 번째 솔루션에 대문자 오류가 있다고 생각합니다. 안 UPDATE Pers P읽어 UPDATE pers P?
ubiquibacon

2
이 솔루션을 시도하고 임시 / 두 번째 테이블의 많은 항목에 대해 쿼리 속도가 매우 느려질 수 있습니다. 인덱스 / 기본 키를 사용하여 임시 / 두 번째 테이블을 만들어보십시오 [ dev.mysql.com/doc/refman/5.1/en/create-table-select.html 참조 ]
Alex

@Konerak이 말했듯이 이것이 실제로 가장 좋은 대답은 아닙니다. 아래 BlueRaja의 답변이 가장 좋습니다. 공감대가 동의하는 것 같습니다.
ShatyUT

@Konerak, CREATE TABLE AS SELECT끔찍한 성능을 제공 하지 않습니까?
Pacerier

27

MySQL에서는 동일한 테이블을 하위 쿼리하여 하나의 테이블을 업데이트 할 수 없습니다.

쿼리를 두 부분으로 분리하거나

 업데이트 TABLE_A AS A
 내부 조인 TABLE_A A에서 B로 A.field1 = B.field1
 SET field2 =? 

5
SELECT ... SET? 나는 이것에 대해 들어 본 적이 없습니다.
S. Serge

@grisson 설명을 주셔서 감사합니다. 이제 IN 절이 작동하지 않는 이유를 알았습니다. 같은 테이블을 대상으로했습니다.
안토니

2
... 실제로는 작동하지 않는 것 같습니다. 여전히 같은 오류가 발생합니다.
BlueRaja-대니 Pflughoeft

2
이 답변은 실제로보다 정확하고 효율적인 작업 AS B을 수행 TABLE_A합니다. 가장 많이 찬성 된 예제의 대답 AS T은 잠재적으로 비효율적 인 대신에 단순화 될 수 있습니다. FROM (SELECT * FROM myTable) AS something다행히도 쿼리 최적화 프로그램은 일반적으로 제거하지만 항상 그렇지는 않습니다.
natbro

23

하위 쿼리에서 임시 테이블 (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'열에 새로운 이름을 지정했습니다.


내부 내부 내부 선택을 수행하는 대신 값을 변수로 선택하지 않겠습니까?
Pacerier 2012

SELECT ( SELECT MAX(gehalt * 1.05)..-첫 번째 SELECT는 열을 선택하지 않습니다.
Istiaque Ahmed

18

아주 간단합니다. 예를 들어, 쓰는 대신 :

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

또는 유사합니다.


13

BlueRaja가 게시 한 접근법은 느리게 테이블에서 중복을 삭제하는 데 사용하면서 수정했습니다. 큰 테이블을 가진 사람에게 도움이되는 경우 Original Query

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)

downvoting하는 경우 의견을 추가하십시오.
Ajak6


3

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에서도 작동합니다 (예 : 액세스)

출처 : 나 자신을 시도


3

MariaDB는 10.3.x ( DELETEUPDATE) 에서 이것을 시작했습니다 .

업데이트-소스 및 대상이 동일한 명령문

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

삭제-동일한 소스 및 대상 테이블

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

다른 해결 방법은 하위 쿼리에서 SELECT DISTINCT 또는 LIMIT를 사용하는 것을 포함하지만, 구체화에 미치는 영향은 명시 적이 지 않습니다. 이것은 나를 위해 일했다

MySql Doc에서 언급했듯이

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.