MySQL ลบจากแบบสอบถามย่อยเป็นเงื่อนไข


87

ฉันกำลังพยายามทำแบบสอบถามดังนี้:

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

อย่างที่คุณบอกได้ฉันต้องการลบความสัมพันธ์ผู้ปกครองกับ 1015 หาก tid เดียวกันมีผู้ปกครองอื่น อย่างไรก็ตามนั่นทำให้ฉันมีข้อผิดพลาดทางไวยากรณ์:

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

ฉันได้ตรวจสอบเอกสารและเรียกใช้แบบสอบถามย่อยด้วยตัวเองและดูเหมือนว่าทั้งหมดจะตรวจสอบ ใครสามารถคิดออกว่ามีอะไรผิดปกติที่นี่?

อัปเดต : ตามคำตอบด้านล่าง MySQL ไม่อนุญาตให้ใช้ตารางที่คุณกำลังลบในแบบสอบถามย่อยสำหรับเงื่อนไข


3
ข้อควรระวัง : คำตอบที่ดีที่ด้านล่างstackoverflow.com/a/4471359/956397เพียงแค่เพิ่มนามแฝงของตารางหลังDELETE t FROM table t ...
PiTheNumber

คำตอบ:


40

คุณไม่สามารถระบุตารางเป้าหมายสำหรับการลบ

วิธีแก้ปัญหาชั่วคราว

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

เราทั้งคู่พูดถูก - ดูความคิดเห็นของเขาต่อคำตอบของฉันด้านล่าง ไวยากรณ์และตรรกะของนามแฝงเป็นปัญหาทั้งคู่ :)
JNK

ใช่ดูเหมือนว่าการลบผ่านแบบสอบถามย่อยจะไม่สามารถทำได้ใน MySQL ในขณะนี้ขอบคุณที่ลองดู :)
mikl

"DELETE FROM term_hierarchy AS th" ในบรรทัดสุดท้ายนั้นไม่มีปัญหาเหมือนกันหรือ ฉันได้รับข้อผิดพลาดทางไวยากรณ์เหมือนกับ OP
malhal

คุณควรเพิ่ม Index ใน term_hierarchy_backup.tid
Roman Newaza

1
ฉันสามารถตรวจสอบได้ว่าเป็นไปไม่ได้ในปี 2019 บน MariaDB 10.3.14 หรือ MySQL Community Server 5.7.27
alpham8

292

สำหรับคนอื่น ๆ ที่พบว่าคำถามนี้ต้องการลบในขณะที่ใช้แบบสอบถามย่อยฉันปล่อยตัวอย่างนี้ไว้ให้คุณเพื่อชิง MySQL (แม้ว่าบางคนดูเหมือนจะคิดว่าไม่สามารถทำได้):

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

จะทำให้คุณมีข้อผิดพลาด:

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

อย่างไรก็ตามคำถามนี้:

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

จะทำงานได้ดี:

Query OK, 1 row affected (3.91 sec)

รวมคำค้นหาย่อยของคุณไว้ในแบบสอบถามย่อยเพิ่มเติม (ที่นี่ชื่อ x) แล้ว MySQL จะทำสิ่งที่คุณถามอย่างมีความสุข


10
ใช้เวลาพอสมควร แต่ฉันก็ใช้งานได้ สำคัญ: 1) ตารางแรกจะต้องใช้นามแฝงตามที่แสดงไว้ที่นี่ด้วย "e" 2) "x" ที่ท้ายไม่ใช่ตัวยึดตำแหน่งเป็นนามแฝงสำหรับตารางชั่วคราวที่สร้างโดยการสืบค้นย่อย "(SELECT id FROM tableE WHERE arg = 1 AND foo = 'bar') "
Tilman Hausherr

3
ทำไมถึงได้ผล? สิ่งนี้เปลี่ยนแปลงไปมากสำหรับฉัน แต่ยิ่งไปกว่านั้นมันไม่ควรได้ผล มันไม่ทำงาน แต่มันไม่ควร
donatJ

1
เหลือเชื่อ. ใช้งานได้จริง! แต่คุณไม่ได้บังคับให้ใช้นามแฝงตารางด้วย e ... คุณสามารถใช้นามแฝงที่คุณต้องการได้
Andrei Sandulescu

1
@jakabadambalazs เหตุผลของฉันเมื่อคิดขึ้นมาก็คือการสืบค้นย่อยที่ขึ้นต้นด้วย "SELECT id" เสร็จสิ้นและส่งคืนรายการรหัสดังนั้นจึงปลดล็อกตารางที่คุณต้องการลบออก
CodeReaper

9
@jakabadambalazs: เราไม่สามารถใช้ตารางเดียวกัน ( e) ใน DELETE และในรายการย่อยได้ เราสามารถแต่ใช้ย่อย-SELECT เพื่อสร้างตารางชั่วคราว ( x) และการใช้งานที่สำหรับย่อยเลือก
Steve Almond

40

ควรรวมนามแฝงไว้หลังDELETEคำสำคัญ:

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
นี่คือคำตอบที่ดี การใช้นามแฝงที่เหมาะสมจะช่วยแก้ปัญหาที่คล้ายกับโพสต์ต้นฉบับได้อย่างยาวนาน (like Mine.)
usumoio

11

คุณต้องอ้างถึงนามแฝงอีกครั้งในคำสั่งลบเช่น:

DELETE th FROM term_hierarchy AS th
....

ตามที่ระบุไว้ที่นี่ในเอกสาร MySQL


ไม่เกี่ยวกับนามแฝงโปรดตรวจสอบ OP อีกครั้ง
ajreal

@ajreal - ฉันทำแล้วและโปรดสังเกตว่าข้อผิดพลาดเริ่มต้นที่คำจำกัดความของนามแฝงและเอกสาร MySQL ระบุอย่างชัดเจนว่าคุณต้องใช้นามแฝงในคำสั่ง DELETE รวมทั้งส่วนคำสั่ง FROM ขอบคุณสำหรับการโหวตลงคะแนน
JNK

เพียงแค่ทำdelete from your_table as t1 where t1.id in(select t2.id from your_table t2);สิ่งนี้คุณได้อะไร?
ajreal

4
เอกสารระบุชัดเจน; 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
คุณไม่ต้องแก้ไขนามแฝงเพียงไม่ระบุตารางเป้าหมายสำหรับการเลือกในการลบ ... นี่คือปัญหาที่แท้จริง
ajreal

7

ฉันเข้าหาสิ่งนี้ด้วยวิธีที่ต่างออกไปเล็กน้อยและมันก็ได้ผลสำหรับฉัน

ฉันจำเป็นต้องลบออกsecure_linksจากตารางของฉันที่อ้างถึงconditionsตารางโดยที่ไม่มีแถวเงื่อนไขเหลืออยู่อีกต่อไป สคริปต์การดูแลทำความสะอาดโดยทั่วไป สิ่งนี้ทำให้ฉันเกิดข้อผิดพลาด - คุณไม่สามารถระบุตารางเป้าหมายสำหรับการลบได้

ดังนั้นการมองหาแรงบันดาลใจที่นี่ฉันจึงได้มาพร้อมกับข้อความค้นหาด้านล่างและมันก็ใช้ได้ดี เนื่องจากสร้างตารางชั่วคราวsl1ที่ใช้เป็นข้อมูลอ้างอิงสำหรับ 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`
            )

ใช้ได้ผลสำหรับฉัน


สำเนาจาก @CodeReaper ด้านบน ... good call tho ... ;)
jrypkahauer

5

ประโยค "ใน" ในการลบไม่ใช่หรือ ... โดยที่ไม่มีประสิทธิภาพอย่างยิ่งหากจะมีการส่งคืนค่าจำนวนมากจากการสืบค้นย่อย ไม่แน่ใจว่าทำไมคุณไม่เพียงแค่ภายใน (หรือขวา) กลับเข้าร่วมกับตารางเดิมจากการสืบค้นย่อยใน ID เพื่อลบแทนที่จะเป็นเราที่ "in (subquery)"?

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)

และบางทีอาจมีคำตอบใน "MySQL ไม่อนุญาต" อย่างไรก็ตามมันใช้งานได้ดีสำหรับฉันที่ให้มาฉันแน่ใจว่าได้ชี้แจงสิ่งที่จะลบอย่างครบถ้วน (ลบ T จากเป้าหมายเป็น T) ลบด้วย Join in MySQLชี้แจงปัญหา DELETE / JOIN


2

หากคุณต้องการทำสิ่งนี้กับ 2 แบบสอบถามคุณสามารถทำสิ่งที่คล้ายกันนี้ได้เสมอ:

1) คว้ารหัสจากตารางด้วย:

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

จากนั้นคัดลอกผลลัพธ์ด้วยเมาส์ / คีย์บอร์ดหรือภาษาโปรแกรมไปที่ XXX ด้านล่าง:

2) DELETE FROM your_table WHERE id IN ( XXX )

บางทีคุณอาจทำได้ในแบบสอบถามเดียว แต่นี่คือสิ่งที่ฉันต้องการ


0

@CodeReaper, @BennyHill: มันทำงานได้ตามที่คาดไว้

อย่างไรก็ตามฉันสงสัยว่าความซับซ้อนของเวลาในการมีแถวนับล้านในตารางหรือไม่? เห็นได้ชัดว่าต้องใช้เวลา5msในการดำเนินการเพื่อให้มีระเบียน 5k บนตารางที่จัดทำดัชนีอย่างถูกต้อง

คำถามของฉัน:

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

คุณสามารถใช้นามแฝงด้วยวิธีนี้ในคำสั่งลบ

DELETE  th.*
FROM term_hierarchy th
INNER JOIN term_hierarchy th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th.parent = 1015;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.