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


593

story_category데이터베이스에 손상된 항목 이있는 테이블 이 있습니다. 다음 쿼리는 손상된 항목을 반환합니다.

SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);

나는 실행을 삭제하려고 시도했다.

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category 
      INNER JOIN story_category ON category_id=category.id);

그러나 다음 오류가 발생합니다.

# 1093-FROM 절에서 업데이트 대상 테이블 'story_category'를 지정할 수 없습니다

이걸 어떻게 극복 할 수 있습니까?




답변:


713

업데이트 :이 답변은 일반적인 오류 분류를 다룹니다. OP의 정확한 쿼리를 가장 잘 처리하는 방법에 대한 자세한 답변은이 질문에 대한 다른 답변을 참조하십시오.

MySQL에서는 SELECT 파트에서 사용하는 것과 동일한 테이블을 수정할 수 없습니다.
이 동작은 http://dev.mysql.com/doc/refman/5.6/en/update.html에 설명되어 있습니다.

어쩌면 당신은 테이블 자체를 조인 할 수 있습니다

논리가 쿼리를 재구성 할 수있을 정도로 단순하면 적절한 선택 기준을 사용하여 하위 쿼리를 잃고 테이블을 조인합니다. 이로 인해 MySQL은 테이블을 두 가지로 간주하여 파괴적인 변화를 진행할 수 있습니다.

UPDATE tbl AS a
INNER JOIN tbl AS b ON ....
SET a.col = b.col

또는 부속 쿼리를 from 절에 더 깊이 중첩시켜보십시오 ...

하위 쿼리가 절대적으로 필요한 경우 해결 방법이 있지만 성능을 포함하여 몇 가지 이유로 추한 것입니다.

UPDATE tbl SET col = (
  SELECT ... FROM (SELECT.... FROM) AS x);

FROM 절의 중첩 된 하위 쿼리는 암시 적 임시 테이블을 생성 하므로 업데이트하는 동일한 테이블로 계산되지 않습니다.

...하지만 쿼리 최적화 프로그램을 조심하십시오

그러나 MySQL 5.7.6 부터 옵티마이 저는 하위 쿼리를 최적화하고 여전히 오류를 줄 수 있습니다. 다행히 optimizer_switch변수를 사용하여이 동작을 끌 수 있습니다. 단기 수정 또는 소규모 일회성 작업 이외의 다른 작업으로는 권장하지 않습니다.

SET optimizer_switch = 'derived_merge=off';

의견 에이 조언을 준 Peter V. Mørch 에게 감사드립니다 .

예시 기술은 원래 Nabble에 출판 된 Baron Schwartz의 기술 입니다.


1
항목을 삭제해야하고 다른 테이블에서 정보를 얻을 수 없었고 동일한 테이블에서 하위 쿼리를 수행해야했기 때문에이 답변을 피했습니다. 이것이 오류를 인터넷 검색하는 동안 상단에 표시되는 것이므로 이것이 나와 가장 적합한 답변이 될 것입니다. 많은 사람들이 동일한 테이블에서 하위 쿼리를하면서 업데이트하려고합니다.
HMR December

2
@Cheekysoft, 왜 대신 변수에 값을 저장하지 않습니까?
Pacerier

19
MySQL 5.7.6 부터 옵티마이 저는 다음과 같은 경우를 제외하고 하위 쿼리를 최적화하고 여전히 오류를 발생시킬 수 있습니다 SET optimizer_switch = 'derived_merge=off';:-(
Peter V. Mørch

1
PeterV.Mørch는 다음 @ mysqlserverteam.com/derived-tables-in-mysql-5-7을 , 경우에 특정 작업이 발생할 수 병합 수행한다. 예를 들어, 파생 더미 테이블에 LIMIT (무한도)를 제공하면 오류가 발생하지 않습니다. 그럼에도 불구하고 여전히 해킹 적이며 향후 버전의 MySQL이 LIMIT와 쿼리 병합을 지원할 위험이 여전히 있습니다.
user2180613

이 해결 방법에 대한 전체 예를 제공 할 수 있습니까? 업데이트 tbl SET col = (SELECT ... FROM (SELECT .... FROM) AS x); 여전히 오류가 발생합니다
JoelBonetR

310

NexusRex 는 동일한 테이블에서 조인을 사용하여 삭제 하는 데 매우 유용한 솔루션 을 제공했습니다 .

이렇게하면 :

DELETE FROM story_category
WHERE category_id NOT IN (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
)

오류가 발생합니다.

그러나 조건을 한 번 더 선택하면 다음을 선택하십시오.

DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
    ) AS c
)

그것은 옳은 일을 할 것입니다 !!

Explanation : 쿼리 최적화 프로그램이 첫 번째 쿼리에 대해 파생 된 병합 최적화 를 수행하지만 (오류로 인해 실패 함) 두 번째 쿼리는 파생 된 병합 최적화에 적합하지 않습니다 . 따라서 옵티마이 저는 먼저 서브 쿼리를 실행해야합니다.


6
어쩌면 그것은 오늘 바인드에 있기 때문일 수도 있지만, 이것이 "최고"가 아닐지라도 이것이 가장 쉬운 대답이었습니다.
Tyler V.

4
감사합니다! 여기 논리는 무엇입니까? 하나 이상의 레벨로 중첩되면 외부 부분보다 먼저 실행됩니까? 그리고 중첩되어 있지 않으면 삭제가 테이블에 잠금이 설정된 후 mySQL이 실행하려고합니까?
Flat Cat

43
이 오류와 솔루션은 논리적으로 의미가 없지만 작동합니다. 때로는 MySQL 개발자들이 어떤 약을 복용하는지 궁금합니다.
Cerin

1
@Cerin.에 동의하십시오. 이것은 완전히 터무니 없지만 작동합니다.
FastTrack

@ekonoval 해결책에 감사드립니다. 그러나 저에게는 최소한의 의미가 없습니다. MySQL을 속이는 것처럼 보이며 그는 그것을 받아들입니다. lol
deFreitas

106

inner join귀하의 하위 쿼리는 불필요하다. 가 표에 없는 story_category곳의 항목을 삭제하려는 것 같습니다 .category_idcategory

이 작업을 수행:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category);

그것 대신에:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN
         story_category ON category_id=category.id);

5
이것이 최고의 답변이어야합니다! 첫 번째 "대신"을 삭제하십시오.
hoyhoy

1
나는 DISTINCT더 나은 성능을 위해 여기에서 불필요 하다고 생각 합니다.).
shA.t

1
나는 미쳐야한다. 대답은 원본에 명시된 것과 동일합니다.
Jeff Lowery

이것은 나에게도 답입니다. where inID 열에 있지 않으므로 기본 테이블을 하위 쿼리하지 않아도됩니다.
Richard

@JeffLowery- 첫 번째 코드 블록이 여기에 대한 답입니다. 질문에서 나온 두 번째 코드 블록.
ToolmakerSteve

95

최근에 같은 테이블에서 레코드를 업데이트해야했습니다. 아래와 같이했습니다.

UPDATE skills AS s, (SELECT id  FROM skills WHERE type = 'Programming') AS p
SET s.type = 'Development' 
WHERE s.id = p.id;

11
이처럼 쓸 수 UPDATE skills SET type='Development' WHERE type='Programming';없습니까? 이것은 원래 질문에 대답하지 않는 것 같습니다.
lilbyrdie

1
과잉 인 것 같지만 @lilbyrdie는 정확합니다 UPDATE skills SET type='Development' WHERE type='Programming';. 나는 왜 그렇게 많은 사람들이 자신이하는 일에 대해 생각하지 않는지 이해하지 못합니다.
shadyyx

1
이것이 가장 좋은 대답입니다, IMHO. 구문은 이해하기 쉽고 이전 문장을 재사용 할 수 있으며 일부 특수한 경우에만 국한되지 않습니다.
Steffen Winkler

2
예제 사례에 의문의 여지가 있더라도 실제 시나리오에서 이해하고 배포하는 것이 가장 좋습니다. 표시된 쿼리는 테이블 목록의 암시 적 조인이 하위 쿼리에 대한 임시 테이블을 생성하므로 안정적인 결과를 제공하고 동일한 테이블의 검색과 수정 사이의 충돌을 피하기 때문에 작동합니다.
AnrDaemon

1
이 과잉 행동에 대해 다른 사람이 무엇을 말할지라도 제목에 대한 질문에 여전히 대답합니다. 중첩 쿼리에서 동일한 테이블을 사용하여 업데이트를 시도 할 때 OP에서 언급 한 오류도 발생합니다.
smac89

35
DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category INNER JOIN story_category ON category_id=category.id
    ) AS c
)

3
왜 이것이 작동하는지, 왜 하나 이상의 레벨을 중첩시키는 지 설명 할 수 있습니까? 이 질문은 @EkoNoval의 질문에 대한 주석으로 이미 요청되었지만 아무도 응답하지 않았습니다. 어쩌면 당신이 도울 수 있습니다.
Akshay Arora

@ AkshayArora, @Cheekysoft의 답변에서 '아마도 당신은 테이블을 자체에 조인 할 수 있습니다'라는 제목 아래 부분을 살펴보십시오. 그는 말했다 UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col-이것은 동일한 테이블에 대한 다른 별칭으로 사용됩니다 여기에 사용됩니다. @NexusRex의 대답과 마찬가지로 첫 번째 SELECT쿼리는 story_category두 번째로 사용되는 파생 테이블 역할을합니다 . 따라서 OP에 언급 된 오류는 여기서 발생해서는 안됩니다.
Istiaque Ahmed

31

할 수 없다면

UPDATE table SET a=value WHERE x IN
    (SELECT x FROM table WHERE condition);

같은 테이블이기 때문에 속임수를 쓸 수 있습니다.

UPDATE table SET a=value WHERE x IN
    (SELECT * FROM (SELECT x FROM table WHERE condition) as t)

[업데이트 또는 삭제 또는 무엇이든]


위의 모든 답변을 가장 이해하기 쉽고 논리적으로 구현합니다. 간단하고 요점.
Clain Dsilva

이것은 이제 내 워크 플로의 일부가되었습니다. 이 오류가 발생하면이 답변으로 이동하여 쿼리를 수정하십시오. 감사.
Vaibhav

13

이것은 하나 이상의 행에 Priority = 1이 포함되도록 테이블의 하위 쿼리를 사용하여 WHERE 절과 테이블에서> = 1 인 경우 Priority 열 값을 1로 업데이트하기 위해 수행 한 작업입니다. 업데이트를 수행하는 동안 확인할 조건) :


UPDATE My_Table
SET Priority=Priority + 1
WHERE Priority >= 1
AND (SELECT TRUE FROM (SELECT * FROM My_Table WHERE Priority=1 LIMIT 1) as t);

나는 그것이 못생긴다는 ​​것을 알고 있지만 잘 작동합니다.


1
@anonymous_reviewer : 다른 사람의 의견에 [-1] 또는 [+1]을 제공하는 경우 그 이유를 언급하십시오. 감사!!!
sactiw

1
-1 올바르지 않기 때문에. SELECT 문에서 사용하는 것과 동일한 테이블을 수정할 수 없습니다.
Chris

1
@Chris MySQL에서 확인했으며 나에게 잘 작동하므로 끝에서 확인한 다음 정확하거나 잘못되었다고 주장하십시오. 감사!!!
sactiw

1
이 페이지의 맨 아래에는 '현재 테이블을 업데이트 할 수 없으며 하위 쿼리의 동일한 테이블에서 선택할 수 없습니다'라고 표시되어 있습니다. -나는 이것을 여러 번 사실로 경험했습니다. dev.mysql.com/doc/refman/5.0/en/update.html
Chris

19
@Chris 나는 그것을 알고 있지만 그 해결 방법이 있으며 정확히 'UPDATE'쿼리로 표시하려고 시도했지만 제대로 작동한다고 믿습니다. 나는 당신이 정말로 내 쿼리를 검증하려고 시도하지 않았다고 생각합니다.
sactiw

6

이를 수행하는 가장 간단한 방법은 하위 쿼리 내에서 상위 쿼리 테이블을 참조 할 때 테이블 별칭을 사용하는 것입니다.

예 :

insert into xxx_tab (trans_id) values ((select max(trans_id)+1 from xxx_tab));

다음으로 변경하십시오.

insert into xxx_tab (trans_id) values ((select max(P.trans_id)+1 from xxx_tab P));

5

원하는 행의 ID를 임시 테이블에 삽입 한 다음 해당 테이블에있는 모든 행을 삭제할 수 있습니다.

@Cheekysoft가 두 단계로 수행한다는 의미 일 수 있습니다.


3

에 따르면 MySQL의 UPDATE 구문 @CheekySoft로 연결, 그것은 바로 아래에 말했다.

현재 테이블을 업데이트하고 하위 쿼리의 동일한 테이블에서 선택할 수 없습니다.

조합에서 여전히 선택하는 동안 store_category에서 삭제하고 있다고 생각합니다.


3

OP가 달성하려는 특정 쿼리의 경우 가장 효율적이고 효율적인 방법은 하위 쿼리를 사용하지 않는 것입니다.

다음은 LEFT JOINOP의 두 쿼리 버전입니다.

SELECT s.* 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

참고 : DELETE s삭제 조작을 story_category테이블로 제한 합니다.
선적 서류 비치

DELETE s 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

1
더 많은 투표권이 없다는 것에 놀랐습니다. 또한 다중 테이블 구문은 UPDATE명령문 및 조인 된 하위 쿼리와 함께 작동 합니다. LEFT JOIN ( SELECT ... )와는 반대로 수행 할 수 있으므로 WHERE IN( SELECT ... )많은 사용 사례에서 구현이 유용합니다.
fyrye

2

앞문을 통해 올 때 무언가가 작동하지 않으면 뒷문을 가져 가십시오.

drop table if exists apples;
create table if not exists apples(variety char(10) primary key, price int);

insert into apples values('fuji', 5), ('gala', 6);

drop table if exists apples_new;
create table if not exists apples_new like apples;
insert into apples_new select * from apples;

update apples_new
    set price = (select price from apples where variety = 'gala')
    where variety = 'fuji';
rename table apples to apples_orig;
rename table apples_new to apples;
drop table apples_orig;

빠릅니다. 데이터가 클수록 좋습니다.


11
그리고 당신은 모든 외래 키를 잃어 버렸고 아마도 계단식 삭제가있을 수 있습니다.
Walf


2

이 시도

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc;

1

이 쿼리가 도움이되기를 바랍니다.

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL

결과는 다음과 같습니다. '# 1064-SQL 구문에 오류가 있습니다. 'LEFT JOIN (SELECT categories.id FROM 카테고리) cat ON story_category.id = cat'근처에서 사용할 올바른 구문은 MariaDB 서버 버전에 해당하는 매뉴얼을 확인하십시오. 1 호선에서
Istiaque Ahmed

다중 테이블 삭제를 사용하는 경우 영향을받는 테이블을 지정해야합니다. DELETE story_category FROM ...그러나 조인 된 하위 쿼리는이 컨텍스트에서 필요하지 않으며를 사용하여 수행 할 수 있습니다 LEFT JOIN category AS cat ON cat.id = story_category.category_id WHERE cat.id IS NULL. 답변의 조인 기준이 잘못 참조 story_category.id = cat.id
됨을

0

에 관한 한,에 story_category존재하지 않는 행을 삭제하려고합니다 category.

삭제할 행을 식별하기위한 원래 쿼리는 다음과 같습니다.

SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id
);

원래 테이블의 NOT IN하위 쿼리와 결합하면 JOIN불필요하게 복잡하게 보입니다. 이는 not exists상관 하위 쿼리 를 사용하여보다 간단하게 표현할 수 있습니다 .

select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id);

이제 이것을 delete문장 으로 바꾸는 것이 쉽습니다 .

delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);    

이 퀘스트는 모든 MySQL 버전과 내가 아는 다른 대부분의 데이터베이스에서 실행됩니다.

DB Fiddle 데모 :

-- set-up
create table story_category(category_id int);
create table category (id int);
insert into story_category values (1), (2), (3), (4), (5);
insert into category values (4), (5), (6), (7);

-- your original query to identify offending rows
SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);
| category_id |
| ---------- : |
| 1 |
| 2 |
| 3 |
-- a functionally-equivalent, simpler query for this
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id)
| category_id |
| ---------- : |
| 1 |
| 2 |
| 3 |
-- the delete query
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);

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