T-SQL: Chọn các hàng để xóa thông qua các phép nối


494

Kịch bản:

Giả sử tôi có hai bảng, TableA và TableB. Khóa chính của TableB là một cột đơn (BId) và là cột khóa ngoại trong TableA.

Trong tình huống của tôi, tôi muốn xóa tất cả các hàng trong TableA được liên kết với các hàng cụ thể trong TableB: Tôi có thể làm điều đó thông qua các phép nối không? Xóa tất cả các hàng được kéo vào từ các liên kết?

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

Hoặc tôi bị buộc phải làm điều này:

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

Lý do tôi hỏi là dường như với tôi rằng tùy chọn đầu tiên sẽ hiệu quả hơn nhiều khi xử lý các bảng lớn hơn.

Cảm ơn!

Câu trả lời:


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

nên làm việc


1
Tôi đã sử dụng Và [điều kiện lọc của tôi] trên phép nối thay vì mệnh đề Where. Tôi sẽ tưởng tượng cả hai sẽ hoạt động, nhưng điều kiện lọc trên phép nối sẽ giới hạn kết quả của bạn từ phép nối.
TheTXI

10
Một câu hỏi. Tại sao chúng ta cần phải viết 'XÓA bảng TỪ' thay vì 'XÓA TỪ'? Tôi thấy nó chỉ hoạt động trong trường hợp này, nhưng tại sao?
LaBracca

66
Tôi nghĩ bởi vì bạn phải chỉ ra bảng nào để xóa hồ sơ. Tôi vừa chạy một truy vấn với cú pháp DELETE TableA, TableB ...và thực sự đã xóa các bản ghi liên quan từ cả hai. Đẹp.
Andrew

1
Trong cú pháp PostgreSQL với phép nối không hoạt động nhưng có thể sử dụng từ khóa "bằng cách sử dụng". DELETE from TableA a using TableB b where b.Bid = a.Bid and [my filter condition]
bartolo-otrit

8
Trong MySQL, bạn sẽ gặp lỗi "Bảng không xác định 'TableA' trong MULTI DELETE" và đó là do bạn đã khai báo bí danh cho TableA (a). Điều chỉnh nhỏ:DELETE a FROM TableA a INNER JOIN TableB b on b.Bid = a.Bid and [my filter condition]
masam

260

Tôi sẽ sử dụng cú pháp này

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

7
Tôi thích cú pháp này là tốt, dường như có ý nghĩa hơn một chút về mặt logic những gì đang diễn ra. Ngoài ra, tôi biết bạn có thể sử dụng loại cú pháp tương tự cho CẬP NHẬT.
Adam Nofsinger

Tôi cũng thích nó, bởi vì vị trí của bí danh bảng sau khi XÓA luôn có vẻ trực quan hơn đối với tôi về những gì đang bị xóa.
Jagd

14
Thật vậy, điều này được ưa thích cho tôi là tốt. Cụ thể trong trường hợp tôi cần thực sự tham gia trên cùng một bảng (ví dụ: để xóa các bản ghi trùng lặp). Trong trường hợp đó, tôi cần sử dụng một bí danh cho "bên" tôi đang xóa và cú pháp này làm cho nó cực kỳ rõ ràng Tôi đang xóa khỏi bí danh trùng lặp.
Chris Simmons

29

Có bạn có thể. Thí dụ :

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

8
Tôi thích tham khảo bảng trong dòng đầu tiên bằng bí danh của nó. Đó là "Xóa một" thay vì "Xóa TableA". Trong trường hợp bạn tham gia bảng với chính nó, nó làm cho nó rõ ràng bạn muốn xóa bên nào.
Jeremy Stein

10

Đã thử làm điều này với cơ sở dữ liệu truy cập và thấy tôi cần sử dụng . * Ngay sau khi xóa.

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

Từ từ chối cấp phát sửa: "Thuộc tính UniqueRecords phải được thiết lập để có, nếu không nó sẽ không làm việc (. Support.microsoft.com/kb/240098 )"
StuperUser

8

Nó gần giống nhau trong MySQL , nhưng bạn phải sử dụng bí danh bảng ngay sau từ "XÓA":

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

2

Cú pháp trên không hoạt động trong Interbase 2007. Thay vào đó, tôi phải sử dụng một cái gì đó như:

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

(Lưu ý Interbase không hỗ trợ từ khóa AS cho bí danh)


2

Tôi đang sử dụng cái này

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

và cách @TheTXI là đủ tốt nhưng tôi đã đọc câu trả lời và nhận xét và tôi thấy một điều phải được trả lời là sử dụng điều kiện trong mệnh đề WHERE hoặc như điều kiện tham gia. Vì vậy, tôi quyết định thử nghiệm nó và viết một đoạn trích nhưng không tìm thấy sự khác biệt có ý nghĩa giữa chúng. Bạn có thể thấy tập lệnh sql ở đây và điểm quan trọng là tôi thích viết nó dưới dạng commnet vì đây không phải là câu trả lời chính xác nhưng nó rất lớn và không thể đưa ra ý kiến, xin vui lòng cho tôi biết.

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

Nếu bạn có thể có được lý do chính đáng từ kịch bản này hoặc viết một hữu ích khác, xin vui lòng chia sẻ. Cảm ơn và hy vọng sự giúp đỡ này.


1

Giả sử bạn có 2 bảng, một bảng có Tập chính (ví dụ: Nhân viên) và một bảng có tập con (ví dụ: Người phụ thuộc) và bạn muốn loại bỏ tất cả các hàng dữ liệu trong bảng Người phụ thuộc không thể khóa với bất kỳ hàng nào trong bảng Master.

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)

Điểm cần chú ý ở đây là bạn chỉ cần thu thập một 'mảng' EmpID từ lần tham gia đầu tiên, sử dụng bộ EmpID đó để thực hiện thao tác Xóa trên bảng Phụ thuộc.


1

Trong SQLite, điều duy nhất hoạt động là một cái gì đó tương tự như câu trả lời của beauXjames.

Dường như điều này xuất hiện DELETE FROM table1 WHERE table1.col1 IN (SOME TEMPORARY TABLE); và một số bảng tạm thời có thể được sắp xếp bằng CHỌN và THAM GIA hai bảng của bạn mà bạn có thể lọc bảng tạm thời này dựa trên điều kiện bạn muốn xóa các bản ghi trong Bảng 1.


1

bạn có thể chạy truy vấn này: -

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

1

Cách đơn giản hơn là:

DELETE TableA
FROM TableB
WHERE TableA.ID = TableB.ID

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

Giảm thiểu việc sử dụng các truy vấn DML với Joins. Bạn sẽ có thể thực hiện hầu hết tất cả các truy vấn DML với các truy vấn con như trên.

Nói chung, các phép nối chỉ nên được sử dụng khi bạn cần CHỌN hoặc NHÓM theo các cột trong 2 hoặc nhiều bảng. Nếu bạn chỉ chạm vào nhiều bảng để xác định dân số, hãy sử dụng các truy vấn con. Đối với các truy vấn XÓA, sử dụng truy vấn con tương quan.

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.