ลบด้วยการเข้าร่วมใน MySQL


501

นี่คือสคริปต์ในการสร้างตารางของฉัน:

CREATE TABLE clients (
   client_i INT(11),
   PRIMARY KEY (client_id)
);
CREATE TABLE projects (
   project_id INT(11) UNSIGNED,
   client_id INT(11) UNSIGNED,
   PRIMARY KEY (project_id)
);
CREATE TABLE posts (
   post_id INT(11) UNSIGNED,
   project_id INT(11) UNSIGNED,
   PRIMARY KEY (post_id)
);

ในรหัส PHP ของฉันเมื่อลบลูกค้าฉันต้องการลบโพสต์โครงการทั้งหมด:

DELETE 
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id;

ตารางการโพสต์ไม่ได้ที่สำคัญต่างประเทศเท่านั้นclient_id project_idฉันต้องการลบโพสต์ในโครงการที่ผ่านไปclient_idแล้ว

สิ่งนี้ไม่ทำงานในขณะนี้เนื่องจากไม่มีการลบโพสต์


10
ฉันคิดว่าคำตอบ Yehosef ควรเป็นคำตอบที่ยอมรับได้เนื่องจากเขาใช้เข้าร่วมตามที่คุณถามและมันทำงานได้ดีกว่าการใช้คำสั่ง IN ตามที่ yukondude เสนอ ...
Gerardo Grignoli

2
รูปแบบที่ต้องการคือ a DELETE posts FROM posts JOIN projects ...แทนที่จะเป็นIN (subquery)รูปแบบ (คำตอบจาก Yehosef ให้ตัวอย่างของรูปแบบที่ต้องการ)
spencer7593

@GerardoGrignoli มันทำงานได้ดีขึ้นสำหรับเครื่องยนต์หรือ MySQL รุ่นใดรุ่นหนึ่งโดยเฉพาะ? ไม่มีเหตุผลใดที่ข้อความค้นหาทั้งสองควรทำงานแตกต่างกันเนื่องจาก AFAIK เป็นข้อความที่เหมือนกัน แน่นอนถ้าผมมีนิกเกิลสำหรับทุกเพิ่มประสิทธิภาพการค้นหาของฉันทำอะไรโง่ ....
พอลผัก

คุณยังสามารถใช้aliasสำหรับชื่อตารางและใช้สิ่งนั้นได้
biniam

คำตอบ:


1254

คุณเพียงแค่ต้องระบุว่าคุณต้องการลบรายการจากpostsตาราง:

DELETE posts
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

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


117
ควรสังเกตว่านี่เป็นคำตอบที่ถูกต้องเนื่องจากการเข้าร่วมบังคับให้คุณใช้ "ลบโพสต์จากโพสต์" แทนที่จะเป็น "ปกติลบจากโพสต์" เนื่องจากตารางที่จะลบไม่มีความชัดเจนอีกต่อไป ขอบคุณ!
siannopollo

8
หมายเหตุคุณไม่สามารถใช้วิธี 'เป็น' ได้ที่นี่เช่นโครงการเข้าร่วมภายในเป็น p ใน p.project_id ...
zzapper

14
จริงๆแล้วคุณสามารถใช้นามแฝงสำหรับตารางที่เข้าร่วม แต่ไม่ใช่สำหรับตารางหลัก (โพสต์) "ลบโพสต์จากโพสต์เข้าร่วมภายในโครงการ p on p.project_id = posts.project_id"
Weboide


14
นี่เป็นคำตอบที่ดีที่สุดเพราะคุณสามารถลบได้จากทั้งสองตารางในการกระทำเดียวDELETE posts , projects FROM posts INNER JOIN projects ON projects.project_id = posts.project_id WHERE projects.client_id = :client_id
Developerium

84

เนื่องจากคุณกำลังเลือกหลายตารางตารางที่จะลบออกจะไม่โปร่งใสอีกต่อไป คุณต้องเลือก :

DELETE posts FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

ในกรณีนี้table_name1และtable_name2เป็นตารางเดียวกันดังนั้นสิ่งนี้จะทำงาน:

DELETE projects FROM posts INNER JOIN [...]

คุณสามารถลบได้จากทั้งสองตารางหากคุณต้องการ:

DELETE posts, projects FROM posts INNER JOIN [...]

โปรดทราบว่าorder byและใช้limit ไม่ได้กับการลบหลายตารางไม่ทำงานสำหรับการลบหลายตาราง

โปรดทราบด้วยว่าหากคุณประกาศชื่อแทนสำหรับตารางคุณต้องใช้นามแฝงเมื่ออ้างถึงตาราง:

DELETE p FROM posts as p INNER JOIN [...]

ผลงานจาก Carpetsmoker และอื่น ๆ


3
หากคุณต้องการคำตอบที่ดีกว่า - อย่างน้อยคุณก็สามารถทำให้ SQL อ่านง่ายขึ้น การอ้างอิง SQL อื่น ๆ ทั้งหมดในคำถามนี้มีคำหลักที่เป็นตัวพิมพ์ใหญ่ (ยกเว้นที่มี 0 โหวต) ฉันไม่แน่ใจว่าทำไมคุณยกเลิกการแก้ไขของฉันอีกครั้ง
Yehosef

3
@Yehosef มีกลุ่มคนที่พบว่า CAPS จ้องมองอย่างแท้จริง ฉันเชื่อว่าฉันไม่ใช่คนเดียว แต่ฉันก็เห็นไม่กี่คนที่ชอบสไตล์ตัวพิมพ์เล็ก
Pacerier

1
ยุติธรรมพอ - ฉันเคารพสิทธิ์ของคุณในการเขียนคำตอบในสไตล์ที่คุณชอบ;)
Yehosef

7
ในการเพิ่มการอภิปรายแบบไม่มีแคปจริงคุณสามารถใช้สไตล์ใดก็ได้ตามที่คุณต้องการ แต่ในคำตอบของคุณคุณกำลังผสมสไตล์ที่คุณสะดวก - ONเป็นตัวพิมพ์ใหญ่ สำหรับนักพัฒนาที่มีประสบการณ์น้อยกว่าอาจบอกได้ว่ามันโอเคที่จะยุ่งและไม่สอดคล้องกันในแง่ของสไตล์
Shade

16
CAPS KEYWORDS ไม่จ้องมอง พวกเขากำลังทำแบบสอบถามที่อ่านได้;)
Sachem

50

หรือสิ่งเดียวกันกับไวยากรณ์ที่แตกต่างกันเล็กน้อย (IMO ที่เป็นมิตรกว่า):

DELETE FROM posts 
USING posts, projects 
WHERE projects.project_id = posts.project_id AND projects.client_id = :client_id;

BTW เมื่อใช้ mysql โดยใช้การรวมจะเร็วกว่าการค้นหาย่อย ...


การใช้หมายถึงอะไร
CMCDragonkai

1
คำอธิบายที่ดีของUSING: stackoverflow.com/questions/11366006/mysql-on-vs-using
bigtex777

10
@ bigtex777: โปรดทราบว่าคำหลักที่ใช้ในคำสั่ง SELECT นั้นมีส่วนเกี่ยวข้องกับคำหลักเดียวกันในคำสั่ง DELETE เพียงเล็กน้อย ใน SELECTs จะระบุรายการของคอลัมน์ที่จะเข้าร่วมในขณะที่ DELETEs จะเป็นรายการของตารางทั้งหมดในการเข้าร่วม
ivanhoe

42

คุณสามารถใช้ ALIAS เช่นนี้มันใช้งานได้กับฐานข้อมูลของฉัน! t ต้องลบตารางจาก!

DELETE t FROM posts t
INNER JOIN projects p ON t.project_id = p.project_id
AND t.client_id = p.client_id

1
สิ่งนี้มีประโยชน์ในการรวมคีย์ผสมเพื่อหลีกเลี่ยงการทำซ้ำชื่อตาราง
Marquez

1
"จริงๆแล้วคุณสามารถใช้นามแฝงสำหรับตารางที่เข้าร่วม แต่ไม่ใช่สำหรับตารางหลัก (โพสต์) 'ลบโพสต์จากการโพสต์เข้าร่วมโครงการในโครงการ p ON p.project_id = posts.project_id'" - @ Weboide
Jeaf Gilbert

2
ที่จริงแล้ว (อ้างอิงจากdev.mysql.com/doc/refman/5.0/en/delete.html ) "ถ้าคุณประกาศชื่อแทนตารางคุณต้องใช้นามแฝงเมื่ออ้างถึงตาราง: ลบ t1 จากการทดสอบ AS t1 ทดสอบที่ WHERE ... "ดังนั้นการใช้นามแฝงก็ใช้ได้
Peter Bowers

25

ฉันเคยชินกับการแก้ปัญหาย่อยมากกว่านี้ แต่ฉันไม่ได้ลองใน MySQL:

DELETE  FROM posts
WHERE   project_id IN (
            SELECT  project_id
            FROM    projects
            WHERE   client_id = :client_id
        );

85
คุณควรหลีกเลี่ยงการใช้คำสำคัญ IN ใน SQL (แม้ว่าจะง่ายกว่าสำหรับผู้เริ่มต้น) และใช้ JOIN แทน (เมื่อทำได้) เนื่องจากแบบสอบถามย่อยมักทำให้สิ่งต่าง ๆ ช้าลงมาก
user276648

14
สิ่งนี้มีแนวโน้มที่จะทำให้ฐานข้อมูลเสียหายเมื่อมีจำนวนแถวจำนวนมากที่ส่งคืนโดยแบบสอบถามย่อย มันยังช้าอย่างมาก
ราชา

2
@ yukondude แน่นอน "IN" นั้นง่ายต่อการเข้าใจมากกว่า "เข้าร่วม" ในวันที่ 1 และนั่นเป็นเหตุผลว่าทำไมคนที่ไม่คุ้นเคยกับ SQL จริงๆจะต้องเขียน "IN" ทุกที่ในขณะที่พวกเขาสามารถใช้ "เข้าร่วม" ได้ดีขึ้น ( หรือดีกว่าใครมากขึ้นอยู่กับแบบสอบถาม) ฉันจำได้หลายปีที่ผ่านมาแบบสอบถาม SQL เกือบทั้งหมดของฉันจะถูกเขียนใหม่โดยผู้ที่รู้วิธีการเขียนแบบสอบถามที่ดีจริง ๆ นั่นเป็นเหตุผลที่ฉันเพิ่มความคิดเห็นเพื่อหลีกเลี่ยง "IN" เพื่อให้คนรู้ว่าถ้าเป็นไปได้พวกเขาควรหลีกเลี่ยงการใช้มัน
276648

10
เหตุผลที่ฉันมาที่หน้านี้เป็นเพราะแบบสอบถามที่ฉันเขียนด้วยคำสั่ง IN คือสุนัขช้า หลีกเลี่ยงคำตอบที่ยอมรับอย่างแน่นอนที่นี่
MikeKulls

4
เก่าเท่าที่คำถามนี้เป็นสิ่งสำคัญที่จะรู้ว่าทำไมคุณควรใช้เข้าร่วมแทนที่จะเป็น เมื่อที่ที่เงื่อนไขทำงานบนแถวมันจะเรียกใช้แบบสอบถามนั้นภายใน นั่นหมายความว่าหากมี 100 แถวที่ต้องตรวจสอบว่า WHERE ที่ใดแบบสอบถามย่อยนั้นจะถูกเรียกใช้ 100 ครั้ง ในขณะที่เข้าร่วมจะทำงานเพียงครั้งเดียว ดังนั้นเมื่อฐานข้อมูลของคุณใหญ่ขึ้นเรื่อย ๆ เคียวรีนั้นก็จะใช้เวลานานขึ้นและจะเสร็จสิ้น @markus เพียงเพราะบางสิ่งไม่สำคัญไม่ได้หมายความว่าคุณควรเขียนโค้ดที่ไม่ดี การเขียนให้ดีขึ้นจะช่วยให้คุณประหยัดเวลาและปวดหัวในอนาคต :)
RisingSun

11

MySQL ลบบันทึกด้วยการเข้าร่วม

โดยทั่วไปคุณใช้ INNER JOIN ในคำสั่ง SELECT เพื่อเลือกระเบียนจากตารางที่มีระเบียนที่สอดคล้องกันในตารางอื่น ๆ นอกจากนี้เรายังสามารถใช้คำสั่ง INNER JOIN ร่วมกับคำสั่ง DELETE เพื่อลบบันทึกจากตารางและบันทึกที่เกี่ยวข้องในตารางอื่น ๆ เช่นในการลบบันทึกจากทั้งตาราง T1 และ T2 ที่ตรงกับเงื่อนไขเฉพาะคุณใช้คำสั่งต่อไปนี้:

DELETE T1, T2
FROM T1
INNER JOIN T2 ON T1.key = T2.key
WHERE condition

โปรดสังเกตว่าคุณใส่ชื่อตาราง T1 และ T2 ระหว่าง DELETE และ FROM หากคุณไม่ใช้ตาราง T1 คำสั่ง DELETE จะลบเฉพาะระเบียนในตาราง T2 และหากคุณเว้นตาราง T2 จะมีการลบเฉพาะระเบียนในตาราง T1 เท่านั้น

เงื่อนไขการเข้าร่วม T1.key = T2.key ระบุระเบียนที่เกี่ยวข้องในตาราง T2 ที่ต้องลบทิ้ง

เงื่อนไขในส่วนคำสั่ง WHERE ระบุว่าระเบียนใดใน T1 และ T2 ที่จะต้องถูกลบ


11

ลบตารางเดียว:

เพื่อที่จะลบรายการจากpostsตาราง:

DELETE ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

เพื่อที่จะลบรายการจากprojectsตาราง:

DELETE pj 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

เพื่อที่จะลบรายการจากclientsตาราง:

DELETE C
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

ลบตารางหลายรายการ:

ในการลบรายการจากหลายตารางจากผลลัพธ์ที่เข้าร่วมคุณต้องระบุชื่อตารางหลังจากDELETEรายการที่คั่นด้วยเครื่องหมายจุลภาค:

สมมติว่าคุณต้องการลบรายการจากทั้งหมดสามตาราง ( posts, projects, clients) สำหรับลูกค้าโดยเฉพาะ:

DELETE C,pj,ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id


4

อีกวิธีในการลบโดยใช้ตัวเลือกย่อยที่ดีกว่าการใช้INก็คือWHERE EXISTS

DELETE  FROM posts
WHERE   EXISTS ( SELECT  1 
                 FROM    projects
                 WHERE   projects.client_id = posts.client_id);

เหตุผลหนึ่งที่จะใช้แทนการเข้าร่วมเป็นที่DELETEที่มีการห้ามการใช้งานของJOIN LIMITหากคุณต้องการที่จะลบในบล็อกเพื่อที่จะไม่สร้างล็อคตารางเต็มคุณสามารถเพิ่มLIMITใช้DELETE WHERE EXISTSวิธีนี้


1
สามารถเขียนแบบสอบถามนี้ด้วย "นามแฝง" ได้หรือไม่? ไม่ชัดเจนจากไวยากรณ์ว่าpostsภายใน EXISTS () นั้นเหมือนกับpostsแถวที่ถูกลบออกไปอย่างไร (IMHO ต่อไป)
MattBianco

ฉันได้ยินคุณ แต่ไม่อนุญาตให้ใช้นามแฝงในตารางเพื่อลบออก "โพสต์" ในข้อความค้นหาย่อยจะต้องเป็นชื่อตารางแบบเต็มซึ่งหมายความว่าหากคุณต้องการใช้ตารางนั้นอีกครั้งในตัวเลือกย่อยจากคำสั่งย่อยคุณจะต้องใช้นามแฝงที่นั่น
Jim Clouse

1
งานนี้:DELETE p FROM posts p WHERE EXISTS ( SELECT 1 FROM projects WHERE projects.client_id = p.client_id);
MattBianco

4
mysql> INSERT INTO tb1 VALUES(1,1),(2,2),(3,3),(6,60),(7,70),(8,80);

mysql> INSERT INTO tb2 VALUES(1,1),(2,2),(3,3),(4,40),(5,50),(9,90);

ลบบันทึกจากหนึ่งตาราง:

mysql> DELETE tb1 FROM tb1,tb2 WHERE tb1.id= tb2.id;

ลบบันทึกจากทั้งสองตาราง:

mysql> DELETE tb2,tb1 FROM tb2 JOIN tb1 USING(id);

1

หากการเข้าร่วมไม่เหมาะกับคุณคุณอาจลองวิธีนี้ มันมีไว้สำหรับการลบบันทึกเด็กกำพร้าจาก t1 เมื่อไม่ได้ใช้คีย์ต่างประเทศ + เฉพาะที่เงื่อนไข นั่นคือลบระเบียนจาก table1 ที่มีฟิลด์ "code" ที่ว่างเปล่าและไม่มีเรคคอร์ดใน table2 โดยการจับคู่กับฟิลด์ "ชื่อ"

delete table1 from table1 t1 
    where  t1.code = '' 
    and 0=(select count(t2.name) from table2 t2 where t2.name=t1.name);


-3

- โปรดทราบว่าคุณไม่สามารถใช้ชื่อแทนบนตารางที่คุณต้องการลบ

DELETE tbl_pagos_activos_usuario
FROM tbl_pagos_activos_usuario, tbl_usuarios b, tbl_facturas c
Where tbl_pagos_activos_usuario.usuario=b.cedula
and tbl_pagos_activos_usuario.cod=c.cod
and tbl_pagos_activos_usuario.rif=c.identificador
and tbl_pagos_activos_usuario.usuario=c.pay_for
and tbl_pagos_activos_usuario.nconfppto=c.nconfppto
and NOT ISNULL(tbl_pagos_activos_usuario.nconfppto)
and c.estatus=50
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.