Lỗi MySQL 1093 - Không thể chỉ định bảng đích để cập nhật trong mệnh đề TỪ


593

Tôi có một bảng story_categorytrong cơ sở dữ liệu của tôi với các mục bị hỏng. Truy vấn tiếp theo trả về các mục bị hỏng:

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

Tôi đã cố gắng để xóa chúng thực thi:

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

Nhưng tôi nhận được lỗi tiếp theo:

# 1093 - Bạn không thể chỉ định bảng mục tiêu 'Story_carget' để cập nhật trong mệnh đề TỪ

Làm thế nào tôi có thể vượt qua điều này?



2
Có vẻ như yêu cầu tính năng trong trình theo dõi lỗi của MySQL ở đây: không thể cập nhật bảng và chọn từ cùng một bảng trong truy vấn con
Ben Creasy

Câu trả lời:


713

Cập nhật: Câu trả lời này bao gồm phân loại lỗi chung. Để có câu trả lời cụ thể hơn về cách xử lý tốt nhất truy vấn chính xác của OP, vui lòng xem các câu trả lời khác cho câu hỏi này

Trong MySQL, bạn không thể sửa đổi cùng bảng mà bạn sử dụng trong phần CHỌN.
Hành vi này được ghi lại tại: http://dev.mysql.com/doc/refman/5.6/en/update.html

Có lẽ bạn chỉ có thể tham gia bảng để chính nó

Nếu logic đủ đơn giản để định hình lại truy vấn, mất truy vấn con và tham gia bảng vào chính nó, sử dụng các tiêu chí lựa chọn phù hợp. Điều này sẽ khiến MySQL xem bảng là hai thứ khác nhau, cho phép các thay đổi mang tính hủy diệt đi trước.

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

Ngoài ra, hãy thử lồng các truy vấn con sâu hơn vào một mệnh đề từ ...

Nếu bạn thực sự cần truy vấn con, có một cách giải quyết, nhưng nó xấu vì nhiều lý do, bao gồm hiệu suất:

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

Truy vấn con lồng nhau trong mệnh đề TỪ tạo ra một bảng tạm thời ẩn , do đó, nó không được tính là cùng một bảng bạn đang cập nhật.

... nhưng coi chừng trình tối ưu hóa truy vấn

Tuy nhiên, hãy cẩn thận rằng từ MySQL 5.7.6 trở đi, trình tối ưu hóa có thể tối ưu hóa truy vấn con và vẫn gây ra lỗi cho bạn. May mắn thay, optimizer_switchbiến có thể được sử dụng để tắt hành vi này; mặc dù tôi không thể khuyên bạn nên làm điều này như bất cứ điều gì hơn là một sửa chữa ngắn hạn, hoặc cho các nhiệm vụ một lần nhỏ.

SET optimizer_switch = 'derived_merge=off';

Cảm ơn Peter V. Mørch cho lời khuyên này trong các ý kiến.

Kỹ thuật ví dụ là từ Baron Schwartz, ban đầu được xuất bản tại Nabble , được diễn giải và mở rộng ở đây.


1
Nâng cao câu trả lời này vì tôi phải xóa các mục và không thể lấy thông tin từ một bảng khác, phải truy vấn từ cùng một bảng. Vì đây là những gì xuất hiện trên đầu trong khi tìm ra lỗi tôi nhận được nên đây sẽ là câu trả lời phù hợp nhất với tôi và rất nhiều người cố gắng cập nhật trong khi chinh phục từ cùng một bảng.
HMR

2
@Cheekysoft, Tại sao không lưu các giá trị vào các biến thay thế?
Pacerier 24/2/2015

19
Coi chừng, từ MySQL 5.7.6 trở đi , trình tối ưu hóa có thể tối ưu hóa truy vấn phụ và vẫn báo lỗi cho bạn, trừ khi bạn SET optimizer_switch = 'derived_merge=off';:-(
Peter V. Mørch

1
@ PeterV.Mørch Theo dõi mysqlserverteam.com/deriving-tables-in-mysql-5-7 , trong trường hợp một số thao tác được thực hiện, việc sáp nhập không thể xảy ra. Ví dụ, cung cấp bảng giả có nguồn gốc với GIỚI HẠN (tính không chính xác) và lỗi sẽ không bao giờ xảy ra. Mặc dù vậy, điều đó khá hackish và vẫn có nguy cơ các phiên bản tương lai của MySQL sẽ hỗ trợ hợp nhất các truy vấn với LIMIT.
dùng2180613

Bạn có thể, xin vui lòng, cung cấp một ví dụ đầy đủ về cách giải quyết này? CẬP NHẬT tbl SET col = (CHỌN ... TỪ (CHỌN .... TỪ) NHƯ x); Tôi vẫn đang gặp lỗi
JoelBonetR

310

NexusRex cung cấp một giải pháp rất tốt để xóa bằng cách tham gia từ cùng một bảng.

Nếu bạn làm điều này:

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
)

bạn sẽ nhận được một lỗi.

Nhưng nếu bạn bọc điều kiện trong một lựa chọn nữa:

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
)

nó sẽ làm điều đúng đắn !!

Giải thích: Trình tối ưu hóa truy vấn thực hiện tối ưu hóa hợp nhất dẫn xuất cho truy vấn đầu tiên (khiến nó bị lỗi với lỗi), nhưng truy vấn thứ hai không đủ điều kiện để tối ưu hóa hợp nhất dẫn xuất . Do đó trình tối ưu hóa buộc phải thực hiện truy vấn con trước.


6
Có thể đó là vì tôi đang bị ràng buộc ngày hôm nay, nhưng đây là câu trả lời dễ nhất, ngay cả khi nó có thể không phải là câu trả lời "tốt nhất".
Tyler V.

4
Điều này đã làm việc tuyệt vời, cảm ơn! Vậy logic ở đây là gì? Nếu nó lồng thêm một cấp nữa thì nó sẽ được thực hiện trước phần bên ngoài? Và nếu nó không được lồng nhau thì myQuery sẽ cố chạy nó sau khi xóa có khóa trên bàn không?
Mèo phẳng

43
Lỗi và giải pháp này không có ý nghĩa logic ... nhưng nó hoạt động. Đôi khi tôi tự hỏi những loại thuốc mà các nhà phát triển MySQL đang sử dụng ...
Cerin

1
Đồng ý với @Cerin .. điều này hoàn toàn vô lý, nhưng nó hoạt động.
FastTrack

@ekonoval Cảm ơn bạn về giải pháp nhưng nó không có ý nghĩa tối thiểu đối với tôi, có vẻ như bạn đang đánh lừa MySQL và anh ấy chấp nhận nó, lol
deFreitas

106

Các inner jointruy vấn phụ của bạn là không cần thiết. Có vẻ như bạn muốn xóa các mục trong story_categoryđó category_idkhông có trong categorybảng.

Làm cái này:

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

Thay vì đó:

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
Đây phải là câu trả lời hàng đầu! Có thể xóa "thay vì" đầu tiên.
hoyhoy

1
Tôi nghĩ DISTINCTlà không cần thiết ở đây - cho một hiệu suất tốt hơn;).
shA.t

1
Tôi bị điên. Câu trả lời giống như những gì đã nêu trong bản gốc.
Jeff Lowery

Đây cũng là câu trả lời cho tôi. Đừng where invào cột ID, vì vậy bạn không cần truy vấn bảng chính.
Richard

@JeffLowery - khối mã đầu tiên là câu trả lời ở đây; đó là khối mã thứ hai từ câu hỏi.
ToolmakerSteve

95

Gần đây tôi đã phải cập nhật hồ sơ trong cùng một bảng tôi đã làm như dưới đây:

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

11
Điều này không thể được viết là UPDATE skills SET type='Development' WHERE type='Programming';? Điều này dường như không trả lời câu hỏi ban đầu.
lilbyrdie

1
Có vẻ như quá mức cần thiết, @lilbyrdie là chính xác - chỉ có thể là như vậy UPDATE skills SET type='Development' WHERE type='Programming';. Tôi không hiểu tại sao nhiều người không nghĩ về những gì họ làm ...
shadyyx

1
Đây là câu trả lời tốt nhất ở đây, IMHO. Cú pháp rất dễ hiểu, bạn có thể sử dụng lại câu lệnh trước đó và nó không giới hạn trong một số trường hợp siêu cụ thể.
Steffen Winkler

2
Ngay cả khi trường hợp ví dụ là nghi vấn, nguyên tắc là tốt nhất để hiểu và triển khai trong các tình huống trong thế giới thực. Truy vấn được hiển thị hoạt động vì tham gia ngầm định trong danh sách bảng tạo một bảng tạm thời cho truy vấn con, do đó cung cấp kết quả ổn định và tránh xung đột giữa truy xuất và sửa đổi của cùng một bảng.
AnrDaemon

1
bất kể mọi người có thể nói gì về việc này là quá mức cần thiết, nó vẫn trả lời câu hỏi về tiêu đề. Lỗi mà OP đã đề cập cũng gặp phải khi cố cập nhật bằng cách sử dụng cùng một bảng trong truy vấn lồng nhau
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
Bạn có thể giải thích tại sao điều này hoạt động, tại sao chỉ cần lồng thêm một cấp độ hoạt động? Câu hỏi này đã được hỏi như là một nhận xét cho câu hỏi của @ EkoNoval nhưng không ai trả lời. Có lẽ bạn có thể giúp đỡ.
Akshay Arora

@AkshayArora, Đi qua phần dưới tiêu đề 'Có lẽ bạn chỉ cần tham gia bảng vào chính nó' trong câu trả lời của @ Cheekysoft. Ông nói UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col- điều này sẽ hoạt động như một bí danh khác cho cùng một bảng được sử dụng ở đây. Tương tự như vậy trong câu trả lời của @ NexusRex, SELECTtruy vấn đầu tiên hoạt động như một bảng dẫn xuất story_categoryđược sử dụng lần thứ hai. Vì vậy, lỗi được đề cập trong OP không nên diễn ra ở đây, phải không?
Istiaque Ahmed

31

Nếu bạn không thể làm

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

bởi vì nó là cùng một bảng, bạn có thể lừa và làm:

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

[cập nhật hoặc xóa hoặc bất cứ điều gì]


Dễ hiểu nhất và thực hiện hợp lý của tất cả các câu trả lời trên. Ngắn gọn và đúng trọng tâm.
Clain Dsilva

Điều này đã trở thành một phần trong quy trình làm việc của tôi bây giờ. Bị mắc kẹt với lỗi này, đi đến câu trả lời này và khắc phục truy vấn. Cảm ơn.
Vaibhav

13

Đây là những gì tôi đã làm để cập nhật giá trị cột Ưu tiên lên 1 nếu nó> = 1 trong bảng và trong mệnh đề WHERE của nó bằng cách sử dụng truy vấn con trên cùng một bảng để đảm bảo rằng ít nhất một hàng chứa Ưu tiên = 1 (vì đó là điều kiện cần kiểm tra trong khi thực hiện cập nhật):


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

Tôi biết nó hơi xấu nhưng nó hoạt động tốt.


1
@anonymous_Vviewer: Trong trường hợp đưa [-1] hoặc thậm chí [+1] cho nhận xét của ai đó, vui lòng đề cập đến lý do tại sao bạn đưa ra. Cảm ơn!!!
sactiw

1
-1 vì điều này không chính xác. Bạn không thể sửa đổi cùng bảng mà bạn sử dụng trong câu lệnh CHỌN.
Chris

1
@Chris Tôi đã xác minh nó trên MySQL và nó hoạt động tốt với tôi vì vậy tôi sẽ yêu cầu bạn vui lòng xác minh nó ở cuối của bạn và sau đó tuyên bố nó là đúng hoặc không chính xác. Cảm ơn!!!
sactiw

1
Ở dưới cùng của trang này có nội dung 'Hiện tại, bạn không thể cập nhật bảng và chọn từ cùng một bảng trong truy vấn con.' - và tôi đã trải nghiệm điều này là đúng trong nhiều dịp. dev.mysql.com/doc/refman/5.0/en/update.html
Chris

19
@Chris Tôi biết điều đó, nhưng có một cách giải quyết cho điều đó và đó chính xác là những gì tôi đã cố gắng thể hiện với truy vấn 'CẬP NHẬT' của tôi và tin tôi rằng nó hoạt động tốt. Tôi không nghĩ rằng bạn đã thực sự cố gắng xác minh truy vấn của tôi cả.
sactiw

6

Cách đơn giản nhất để làm điều này là sử dụng bí danh bảng khi bạn đang tham chiếu bảng truy vấn cha bên trong truy vấn phụ.

Thí dụ :

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

Thay đổi nó thành:

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

5

Bạn có thể chèn id của các hàng mong muốn vào một bảng tạm thời và sau đó xóa tất cả các hàng được tìm thấy trong bảng đó.

đó có thể là ý nghĩa của @Cheekysoft bằng cách thực hiện theo hai bước.


3

Theo Cú pháp CẬP NHẬT Mysql được liên kết bởi @CheekySoft, nó nói ngay ở phía dưới.

Hiện tại, bạn không thể cập nhật một bảng và chọn từ cùng một bảng trong truy vấn con.

Tôi đoán bạn đang xóa khỏi store_carget trong khi vẫn chọn từ nó trong liên minh.


3

Đối với truy vấn cụ thể mà OP đang cố gắng đạt được, cách lý tưởng và hiệu quả nhất để làm điều này là KHÔNG sử dụng truy vấn con.

Dưới đây là các LEFT JOINphiên bản của hai truy vấn của OP:

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

Lưu ý: DELETE shạn chế thao tác xóa vào story_categorybảng.
Tài liệu

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

1
Ngạc nhiên vì điều này không có nhiều phiếu bầu hơn. Cũng cần lưu ý rằng cú pháp nhiều bảng cũng hoạt động với các UPDATEcâu lệnh và các truy vấn phụ đã tham gia. Cho phép bạn thực hiện LEFT JOIN ( SELECT ... )trái ngược với WHERE IN( SELECT ... ), làm cho việc triển khai trở nên hữu ích trong nhiều trường hợp sử dụng.
fyrye

2

Nếu một cái gì đó không hoạt động, khi đến cửa trước, sau đó đi cửa sau:

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;

Thật nhanh. Dữ liệu càng lớn càng tốt.


11
Và bạn vừa bị mất tất cả các khóa ngoại của mình và có thể bạn cũng có một số thao tác xóa tầng.
Walf

2

Cố gắng lưu kết quả của câu lệnh Chọn trong biến riêng biệt và sau đó sử dụng câu lệnh đó để xóa truy vấn.


2

thử cái này

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

1

Làm thế nào về truy vấn này hy vọng nó sẽ giúp

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

Kết quả cho thấy: '# 1064 - Bạn có lỗi trong cú pháp SQL của mình; kiểm tra hướng dẫn tương ứng với phiên bản máy chủ MariaDB của bạn để biết cú pháp phù hợp để sử dụng gần 'LEFT THAM GIA (CHỌN loại.id TỪ danh mục) cat ON Story_carget.id = cat.' tại dòng 1 '
Istiaque Ahmed

Khi sử dụng xóa nhiều bảng, bạn phải chỉ định các bảng bị ảnh hưởng. DELETE story_category FROM ...tuy nhiên, truy vấn phụ đã tham gia là không cần thiết trong ngữ cảnh này và có thể được thực hiện bằng cách sử dụng LEFT JOIN category AS cat ON cat.id = story_category.category_id WHERE cat.id IS NULL, Lưu ý tiêu chí tham gia trong câu trả lời tham chiếu không chính xácstory_category.id = cat.id
fyrye

0

Theo như quan tâm, bạn muốn xóa các hàng trong story_categoryđó không tồn tại category.

Đây là truy vấn ban đầu của bạn để xác định các hàng cần xóa:

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

Kết hợp NOT INvới một truy vấn con mà JOINbảng ban đầu dường như bị xáo trộn không cần thiết. Điều này có thể được thể hiện theo cách thẳng thắn hơn not existsvà truy vấn con tương quan:

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

Bây giờ thật dễ dàng để biến điều này thành một deletetuyên bố:

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

Quer này sẽ chạy trên bất kỳ phiên bản MySQL nào, cũng như trong hầu hết các cơ sở dữ liệu khác mà tôi biết.

Bản demo trên 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);
| thể loại_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)
| thể loại_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;
| thể loại_id |
| ----------: |
| 4 |
| 5 |
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.