MySQL XÓA TỪ với truy vấn con là điều kiện


85

Tôi đang cố gắng thực hiện một truy vấn như sau:

DELETE FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN (
    SELECT DISTINCT(th1.tid)
    FROM term_hierarchy AS th1
    INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
    WHERE th1.parent = 1015
);

Như bạn có thể nói, tôi muốn xóa mối quan hệ cha mẹ với 1015 nếu cùng một tid có cha mẹ khác. Tuy nhiên, điều đó khiến tôi gặp lỗi cú pháp:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS th
WHERE th.parent = 1015 AND th.tid IN (
  SELECT DISTINCT(th1.tid)
  FROM ter' at line 1

Tôi đã kiểm tra tài liệu và tự chạy truy vấn con, và tất cả dường như đã hoàn tất. Bất cứ ai có thể tìm ra những gì sai ở đây?

Cập nhật : Như đã trả lời bên dưới, MySQL không cho phép sử dụng bảng bạn đang xóa trong một truy vấn con cho điều kiện.


2
Chú ý : Câu trả lời tốt ở dưới cùng stackoverflow.com/a/4471359/956397 chỉ cần thêm bí danh bảng sauDELETE t FROM table t ...
PiTheNumber

Câu trả lời:


39

Bạn không thể chỉ định bảng mục tiêu để xóa.

Một cách giải quyết

create table term_hierarchy_backup (tid int(10)); <- check data type

insert into term_hierarchy_backup 
SELECT DISTINCT(th1.tid)
FROM term_hierarchy AS th1
INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th1.parent = 1015;

DELETE FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN (select tid from term_hierarchy_backup);

chúng tôi đều đúng - hãy xem bình luận của anh ấy cho câu trả lời của tôi bên dưới. Cú pháp bí danh và logic đều vấn đề :)
JNK

Vâng, dường như xóa qua subquery không phải là hiện nay có thể trong MySQL - cảm ơn đã dành một cái nhìn vào nó :)
mikl

không phải "DELETE FROM term_hierarchy AS th" trong dòng cuối cùng đó có cùng một vấn đề? Tôi gặp lỗi cú pháp giống như lỗi OP.
malhal

Bạn nên thêm Index vào term_hierarchy_backup.tid.
Roman Newaza

1
Tôi có thể xác minh rằng, rằng nó không phải là có thể vào năm 2019 trên MariaDB 10.3.14 hoặc MySQL Community Server 5.7.27
alpham8

284

Đối với những người khác nhận thấy câu hỏi này đang tìm cách xóa trong khi sử dụng truy vấn con, tôi để lại cho bạn ví dụ này để đánh dấu ngoài MySQL (ngay cả khi một số người dường như nghĩ rằng nó không thể được thực hiện):

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM tableE
             WHERE arg = 1 AND foo = 'bar');

sẽ cung cấp cho bạn một lỗi:

ERROR 1093 (HY000): You can't specify target table 'e' for update in FROM clause

Tuy nhiên truy vấn này:

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM (SELECT id
                   FROM tableE
                   WHERE arg = 1 AND foo = 'bar') x);

sẽ hoạt động tốt:

Query OK, 1 row affected (3.91 sec)

Kết thúc truy vấn con của bạn trong một truy vấn con bổ sung (ở đây có tên là x) và MySQL sẽ vui vẻ thực hiện những gì bạn yêu cầu.


10
Đã mất một thời gian nhưng tôi đã làm việc. Quan trọng: 1) Bảng đầu tiên phải được đặt bí danh như được hiển thị ở đây với "e", 2) "x" ở cuối không phải là trình giữ chỗ, nó là bí danh cho bảng tạm thời do truy vấn con tạo ra "(SELECT id FROM tableE WHERE arg = 1 AND foo = 'bar') ".
Tilman Hausherr

3
Tại sao điều này hoạt động? Điều này thay đổi rất nhiều đối với tôi, nhưng hơn nữa, nó sẽ không hoạt động. Nó không làm việc, nhưng nó không nên.
donatJ

1
Không thể tin được. điều này thực sự hoạt động! nhưng bạn không bị buộc phải đặt bí danh trong bảng với e ... bạn có thể sử dụng bất kỳ bí danh nào bạn muốn.
Andrei Sandulescu,

1
@jakabadambalazs Lý do của tôi khi nghĩ ra nó, đó là truy vấn con bắt đầu bằng "SELECT id" kết thúc và trả về một danh sách id và do đó giải phóng khóa của bảng mà bạn muốn xóa.
CodeReaper

9
@jakabadambalazs: Chúng tôi không thể sử dụng cùng một bảng ( e) trong DELETE và trong phần CHỌN phụ của nó. Tuy nhiên, chúng ta có thể sử dụng một CHỌN phụ phụ để tạo một bảng tạm thời ( x) và sử dụng cho CHỌN phụ.
Steve Almond

39

Bí danh phải được bao gồm sau DELETEtừ khóa:

DELETE th
FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN 
(
    SELECT DISTINCT(th1.tid)
    FROM term_hierarchy AS th1
    INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
    WHERE th1.parent = 1015
);

3
Đây là một câu trả lời tốt. Đặt biệt hiệu thích hợp sẽ đi một chặng đường dài để giải quyết các vấn đề tương tự như bài viết gốc. (như của tôi.)
usumoio

10

Bạn cần tham khảo lại bí danh trong câu lệnh xóa, như:

DELETE th FROM term_hierarchy AS th
....

Như đã nêu ở đây trong tài liệu MySQL.


không phải về bí danh, vui lòng kiểm tra lại OP
ajreal

@ajreal - Tôi đã làm, và xin lưu ý rằng lỗi bắt đầu ở định nghĩa bí danh và tài liệu MySQL nói rõ bạn cần sử dụng bí danh trong câu lệnh DELETE cũng như mệnh đề FROM. Cảm ơn vì đã ủng hộ.
JNK

đơn giản là làm điều này delete from your_table as t1 where t1.id in(select t2.id from your_table t2);bạn đã nhận được gì?
ajreal

4
Tài liệu ghi rõ; Currently, you cannot delete from a table and select from the same table in a subquery. dev.mysql.com/doc/refman/5.5/en/delete.html
Björn

1
bạn không phải sửa bí danh, chỉ cần không chỉ định bảng mục tiêu để chọn trong xóa ... đây là vấn đề thực sự
ajreal

7

Tôi tiếp cận vấn đề này theo một cách hơi khác và nó phù hợp với tôi;

Tôi cần xóa secure_linkskhỏi bảng của mình đã tham chiếu đến conditionsbảng mà không còn bất kỳ hàng điều kiện nào còn lại. Một kịch bản quản lý nhà về cơ bản. Điều này đã gây ra lỗi cho tôi - Bạn không thể chỉ định bảng mục tiêu để xóa.

Vì vậy, tìm kiếm nguồn cảm hứng ở đây, tôi đã đưa ra truy vấn dưới đây và nó hoạt động tốt. Điều này là do nó tạo ra một bảng tạm thời sl1được sử dụng làm tham chiếu cho DELETE.

DELETE FROM `secure_links` WHERE `secure_links`.`link_id` IN 
            (
            SELECT
                `sl1`.`link_id` 
            FROM 
                (
                SELECT 

                    `sl2`.`link_id` 

                FROM 
                    `secure_links` AS `sl2` 
                    LEFT JOIN `conditions` ON `conditions`.`job` = `sl2`.`job` 

                WHERE 

                    `sl2`.`action` = 'something' AND 
                    `conditions`.`ref` IS NULL 
                ) AS `sl1`
            )

Làm việc cho tôi.


5

Chẳng phải mệnh đề "in" trong xóa ... ở đâu, cực kỳ kém hiệu quả, nếu sẽ có một số lượng lớn giá trị được trả về từ truy vấn con? Không chắc chắn tại sao bạn không chỉ bên trong (hoặc bên phải) tham gia ngược lại bảng gốc từ truy vấn con trên ID để xóa, thay vì chúng tôi "trong (truy vấn con)".?

DELETE T FROM Target AS T
RIGHT JOIN (full subquery already listed for the in() clause in answers above) ` AS TT ON (TT.ID = T.ID)

Và có thể nó được trả lời trong "MySQL không cho phép nó", tuy nhiên, nó đang hoạt động tốt đối với tôi CUNG CẤP Tôi đảm bảo làm rõ đầy đủ những gì cần xóa (XÓA T khỏi Mục tiêu AS T). Xóa bằng Tham gia trong MySQL làm rõ vấn đề XÓA / THAM GIA.


2

Nếu bạn muốn thực hiện việc này với 2 truy vấn, bạn luôn có thể thực hiện điều gì đó tương tự như sau:

1) lấy id từ bảng với:

SELECT group_concat(id) as csv_result FROM your_table WHERE whatever = 'test' ...

Sau đó sao chép kết quả bằng chuột / bàn phím hoặc ngôn ngữ lập trình sang XXX bên dưới:

2) DELETE FROM your_table WHERE id IN ( XXX )

Có thể bạn có thể làm điều này trong một truy vấn, nhưng đây là điều tôi thích.


0

@CodeReaper, @BennyHill: Nó hoạt động như mong đợi.

Tuy nhiên, tôi thắc mắc về độ phức tạp về thời gian khi có hàng triệu hàng trong bảng? Rõ ràng, nó sắp 5msthực thi để có 5k bản ghi trên một bảng được lập chỉ mục chính xác.

Sự truy vấn của tôi:

SET status = '1'
WHERE id IN (
    SELECT id
    FROM (
      SELECT c2.id FROM clusters as c2
      WHERE c2.assign_to_user_id IS NOT NULL
        AND c2.id NOT IN (
         SELECT c1.id FROM clusters AS c1
           LEFT JOIN cluster_flags as cf on c1.last_flag_id = cf.id
           LEFT JOIN flag_types as ft on ft.id = cf.flag_type_id
         WHERE ft.slug = 'closed'
         )
      ) x)```

Or is there something we can improve on my query above?

0

bạn có thể sử dụng bí danh theo cách này trên câu lệnh xóa

DELETE  th.*
FROM term_hierarchy th
INNER JOIN term_hierarchy th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th.parent = 1015;
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.