T-SQL: การเลือกแถวเพื่อลบผ่านการรวม


494

สถานการณ์:

สมมติว่าฉันมีสองตาราง TableA และ TableB คีย์หลักของ TableB คือคอลัมน์เดียว (BId) และเป็นคอลัมน์คีย์ต่างประเทศใน TableA

ในสถานการณ์ของฉันฉันต้องการลบแถวทั้งหมดใน TableA ที่เชื่อมโยงกับแถวที่ระบุใน TableB: ฉันสามารถทำสิ่งนั้นผ่านการรวมได้หรือไม่ ลบแถวทั้งหมดที่ดึงออกจากการรวมหรือไม่

DELETE FROM TableA 
FROM
   TableA a
   INNER JOIN TableB b
      ON b.BId = a.BId
      AND [my filter condition]

หรือฉันถูกบังคับให้ทำเช่นนี้:

DELETE FROM TableA
WHERE
   BId IN (SELECT BId FROM TableB WHERE [my filter condition])

เหตุผลที่ฉันถามก็คือดูเหมือนว่าตัวเลือกแรกจะมีประสิทธิภาพมากขึ้นเมื่อจัดการกับตารางขนาดใหญ่

ขอบคุณ!

คำตอบ:


722
DELETE TableA
FROM   TableA a
       INNER JOIN TableB b
               ON b.Bid = a.Bid
                  AND [my filter condition] 

ควรทำงาน


1
ฉันใช้และ [เงื่อนไขตัวกรองของฉัน] ในการเข้าร่วมแทนที่จะเป็นส่วนคำสั่ง Where ฉันคิดว่าทั้งสองจะทำงานได้ แต่เงื่อนไขตัวกรองในการเข้าร่วมจะ จำกัด ผลลัพธ์ของคุณจากการเข้าร่วม
TheTXI

10
หนึ่งคำถาม. ทำไมเราต้องเขียน 'DELETE TableA FROM' แทนที่จะเป็น 'DELETE FROM' ฉันเห็นมันใช้งานได้เฉพาะในกรณีนี้ แต่ทำไม
LaBracca

66
ฉันคิดว่าเพราะคุณต้องระบุตารางที่จะลบบันทึก ฉันเพิ่งเรียกใช้แบบสอบถามด้วยไวยากรณ์DELETE TableA, TableB ...และที่จริงลบระเบียนที่เกี่ยวข้องจากทั้งสอง ดี
Andrew

1
ในไวยากรณ์ PostgreSQL ด้วยการเข้าร่วมไม่ทำงาน แต่เป็นไปได้ที่จะใช้คำหลัก "using" DELETE from TableA a using TableB b where b.Bid = a.Bid and [my filter condition]
bartolo-otrit

8
ใน MySQL คุณจะได้รับข้อผิดพลาด "ตารางที่ไม่รู้จัก 'TableA' ใน MULTI DELETE" และนั่นเป็นเพราะคุณได้ประกาศชื่อแทนสำหรับ TableA (a) การปรับเล็กน้อย:DELETE a FROM TableA a INNER JOIN TableB b on b.Bid = a.Bid and [my filter condition]
masam

260

ฉันจะใช้ไวยากรณ์นี้

Delete a 
from TableA a
Inner Join TableB b
on  a.BId = b.BId
WHERE [filter condition]

7
ฉันชอบไวยากรณ์นี้เช่นกันดูเหมือนว่าจะมีเหตุผลมากกว่านี้ว่าเกิดอะไรขึ้น นอกจากนี้ฉันรู้ว่าคุณสามารถใช้ไวยากรณ์ชนิดเดียวกันนี้สำหรับการอัปเดต
Adam Nofsinger

ฉันก็ชอบเช่นกันเพราะการวางตำแหน่งของชื่อแทนตารางหลังจาก DELETE ดูเหมือนจะง่ายกว่าสำหรับฉันในสิ่งที่ถูกลบไป
Jagd

14
อันนี้ก็เป็นที่ชื่นชอบสำหรับฉันเช่นกัน โดยเฉพาะในกรณีที่ฉันต้องเข้าร่วมในตารางเดียวกันจริง ๆ (เช่นสำหรับการลบระเบียนที่ซ้ำกัน) ในกรณีนี้ฉันต้องใช้นามแฝงสำหรับ "ด้าน" ที่ฉันลบออกและไวยากรณ์นี้ทำให้ชัดเจนยิ่งขึ้นว่าฉันกำลังลบนามแฝงที่ซ้ำกัน
Chris Simmons

29

ใช่คุณสามารถ. ตัวอย่าง:

DELETE TableA 
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]

8
ฉันชอบอ้างถึงตารางในบรรทัดแรกโดยนามแฝง นั่นคือ "ลบ" แทนที่จะ "ลบ TableA" ในกรณีที่คุณเข้าร่วมตารางด้วยตัวเองมันทำให้ชัดเจนว่าคุณต้องการลบด้านใด
Jeremy Stein

10

พยายามทำสิ่งนี้กับฐานข้อมูลการเข้าถึงและพบว่าฉันต้องการใช้. *ทันทีหลังจากลบ

DELETE a.*
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]

จากการแก้ไขที่ถูกปฏิเสธที่รอดำเนินการ: "คุณสมบัติ UniqueRecords ต้องตั้งค่าเป็นใช่มิฉะนั้นจะไม่ทำงาน ( support.microsoft.com/kb/240098 )"
StuperUser

8

มันเกือบจะเหมือนกันในMySQLแต่คุณต้องใช้นามแฝงของตารางหลังจากคำว่า "DELETE":

DELETE a
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]

2

ไวยากรณ์ข้างต้นไม่สามารถใช้งานได้ใน Interbase 2007 แต่ฉันต้องใช้สิ่งต่อไปนี้:

DELETE FROM TableA a WHERE [filter condition on TableA] 
  AND (a.BId IN (SELECT a.BId FROM TableB b JOIN TableA a 
                 ON a.BId = b.BId 
                 WHERE [filter condition on TableB]))

(หมายเหตุ Interbase ไม่สนับสนุนคำหลัก AS สำหรับชื่อแทน)


2

ฉันใช้สิ่งนี้

DELETE TableA 
FROM TableA a
INNER JOIN
TableB b on b.Bid = a.Bid
AND [condition]

และวิธี @TheXI นั้นดีพอ แต่ฉันอ่านคำตอบและความคิดเห็นและฉันพบว่าสิ่งหนึ่งที่ต้องตอบคือการใช้เงื่อนไขในส่วนคำสั่ง WHERE หรือเงื่อนไขการเข้าร่วม ดังนั้นฉันจึงตัดสินใจทดสอบและเขียนตัวอย่าง แต่ไม่พบความแตกต่างที่มีความหมายระหว่างพวกเขา คุณสามารถดูสคริปต์ sql ที่นี่และจุดสำคัญคือฉันชอบที่จะเขียนมันเป็น commnet เพราะนี่ไม่ใช่คำตอบที่แน่นอน แต่มันมีขนาดใหญ่และไม่สามารถใส่ความคิดเห็นได้โปรดยกโทษให้ฉัน

Declare @TableA  Table
(
  aId INT,
  aName VARCHAR(50),
  bId INT
)
Declare @TableB  Table
(
  bId INT,
  bName VARCHAR(50)  
)

Declare @TableC  Table
(
  cId INT,
  cName VARCHAR(50),
  dId INT
)
Declare @TableD  Table
(
  dId INT,
  dName VARCHAR(50)  
)

DECLARE @StartTime DATETIME;
SELECT @startTime = GETDATE();

DECLARE @i INT;

SET @i = 1;

WHILE @i < 1000000
BEGIN
  INSERT INTO @TableB VALUES(@i, 'nameB:' + CONVERT(VARCHAR, @i))
  INSERT INTO @TableA VALUES(@i+5, 'nameA:' + CONVERT(VARCHAR, @i+5), @i)

  SET @i = @i + 1;
END

SELECT @startTime = GETDATE()

DELETE a
--SELECT *
FROM @TableA a
Inner Join @TableB b
ON  a.BId = b.BId
WHERE a.aName LIKE '%5'

SELECT Duration = DATEDIFF(ms,@StartTime,GETDATE())

SET @i = 1;
WHILE @i < 1000000
BEGIN
  INSERT INTO @TableD VALUES(@i, 'nameB:' + CONVERT(VARCHAR, @i))
  INSERT INTO @TableC VALUES(@i+5, 'nameA:' + CONVERT(VARCHAR, @i+5), @i)

  SET @i = @i + 1;
END

SELECT @startTime = GETDATE()

DELETE c
--SELECT *
FROM @TableC c
Inner Join @TableD d
ON  c.DId = d.DId
AND c.cName LIKE '%5'

SELECT Duration    = DATEDIFF(ms,@StartTime,GETDATE())

หากคุณได้รับเหตุผลที่ดีจากสคริปต์นี้หรือเขียนเป็นประโยชน์อื่นกรุณาแบ่งปัน ขอขอบคุณและหวังว่าความช่วยเหลือนี้


1

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

delete from Dependents where EmpID in (
select d.EmpID from Employees e 
    right join Dependents d on e.EmpID = d.EmpID
    where e.EmpID is null)

จุดสังเกตที่นี่คือคุณเพียงแค่รวบรวม 'อาเรย์' ของ EmpID จากการเข้าร่วมก่อนการใช้ชุดของ EmpIDs เพื่อทำการลบในตารางผู้อยู่ในอุปการะ


1

ใน SQLite สิ่งเดียวที่ใช้ได้คือคำตอบของ beauXjames

ดูเหมือนว่าจะเกิดขึ้นกับสิ่งนี้ DELETE FROM table1 WHERE table1.col1 IN (SOME TEMPORARY TABLE); และตารางชั่วคราวบางอย่างสามารถถูกสร้างขึ้นโดย SELECT และรวมสองตารางของคุณซึ่งคุณสามารถกรองตารางชั่วคราวนี้ตามเงื่อนไขที่คุณต้องการลบระเบียนในตารางที่ 1


1

คุณสามารถเรียกใช้แบบสอบถามนี้: -

Delete from TableA 
from 
TableA a, TableB b 
where a.Bid=b.Bid
AND [my filter condition]


1
DELETE FROM table1
where id IN 
    (SELECT id FROM table2..INNER JOIN..INNER JOIN WHERE etc)

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

โดยทั่วไปการรวมควรใช้เมื่อคุณต้องการเลือกหรือจัดกลุ่มตามคอลัมน์ใน 2 ตารางขึ้นไป หากคุณเพียงตารางหลายสัมผัสในการกำหนดประชากรใช้ subqueries สำหรับเคียวรี DELETE ให้ใช้เคียวรี่ย่อยที่สัมพันธ์กัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.