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

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

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.