2015-06-23 13 views
6

Tôi có hai bảng có cùng số cột không có khóa chính (tôi biết, đây không phải lỗi của tôi). Bây giờ tôi cần phải xóa tất cả các hàng từ bảng A tồn tại trong bảng B (chúng bằng nhau, mỗi cột có 30 cột).DELETE WITH INTERSECT

Cách trực tiếp nhất mà tôi nghĩ là thực hiện INNER JOIN và giải quyết sự cố của mình. Tuy nhiên, viết điều kiện cho tất cả các cột (đáng lo ngại về NULL) không phải là thanh lịch (có thể gây ra các bảng của tôi không thanh lịch hoặc).

Tôi muốn sử dụng INTERSECT. Tôi không biết làm thế nào để làm điều đó? Đây là câu hỏi của tôi đầu tiên:

tôi đã cố gắng (SQL Fiddle):

declare @A table (value int, username varchar(20)) 
declare @B table (value int, username varchar(20)) 

insert into @A values (1, 'User 1'), (2, 'User 2'), (3, 'User 3'), (4, 'User 4') 
insert into @B values (2, 'User 2'), (4, 'User 4'), (5, 'User 5') 

DELETE @A 
    FROM (SELECT * FROM @A INTERSECT SELECT * from @B) A 

Nhưng tất cả các hàng đã bị xóa từ bảng @A.

Điều này dẫn tôi đến câu hỏi thứ hai: tại sao lệnh DELETE @A FROM @B xóa tất cả các hàng khỏi bảng @A?

+0

Nếu tôi nhớ rõ, việc xóa B FROM B không hạn chế bất cứ điều gì và điều này bình thường sẽ xóa mọi thứ. bạn có thể hạn chế bằng cách thực hiện một cái gì đó như: XÓA A FROM B trong đó A.value = B.Value Thêm FROM sau khi xóa giống như LEFT JOIN – Xavier

+4

Bạn có nghĩa là 'DELETE A' và không chạy. Ngay bây giờ bạn đang xóa @A CROSS JOIN (cái gì khác). Mà xóa tất cả mọi thứ nếu có ít nhất một hàng trong cái gì khác. Nhìn vào kế hoạch truy vấn để xem điều này. – usr

Trả lời

9

Hãy thử điều này:

DELETE a 
FROM @A a 
WHERE EXISTS (SELECT a.* INTERSECT SELECT * FROM @B) 

Xóa từ @A nơi, cho mỗi bản ghi trong @A, có một trận đấu mà các kỷ lục trong @A giao cắt với mức kỷ lục trong @B.

Điều này được dựa trên Paul White's blog post sử dụng INTERSECT để kiểm tra sự bất bình đẳng.

SQL Fiddle

+0

Cảm ơn rất nhiều. Đó là cú pháp tôi đang tìm kiếm. Thật khó để chọn câu trả lời đúng vì tôi đã hỏi hai câu hỏi. Nhưng câu trả lời hay nhất cho câu hỏi thứ hai của tôi là trên các ý kiến ​​được đưa ra bởi @usr. – Nizam

4

Để trả lời câu hỏi đầu tiên của bạn, bạn có thể xóa dựa trên join:

delete a 
from @a a 
join @b b on a.value = b.value and a.username = b.username 

Trường hợp thứ hai là thực sự kỳ lạ. Tôi nhớ trường hợp tương tự ở đây và nhiều khiếu nại về hành vi này. Tôi sẽ cố gắng đặt câu hỏi đó.

+0

Cảm ơn rất nhiều. Nó hoạt động nhưng tôi muốn tránh tham gia vì số cột trong trường hợp thực sự của tôi. – Nizam

0
  1. Tạo một bảng (T) xác định khóa chính
  2. chèn tất cả hồ sơ từ A vào T (i sẽ cho rằng không có bản sao ở A)
  3. cố gắng để chèn tất cả hồ sơ từ B trong T 3A. nếu chèn không xóa nó khỏi B (đã tồn tại)
  4. Drop T (bạn thực sự không nên !!!)
3

Bạn có thể sử dụng câu trả lời Giorgi để xóa các hàng bạn cần.

Đối với câu hỏi liên quan đến lý do tại sao tất cả các hàng đã bị xóa, đó là vì không có điều kiện giới hạn. Mệnh đề FROM của bạn nhận được một bảng để xử lý, nhưng không có mệnh đề WHERE để ngăn các hàng nhất định khỏi bị xóa khỏi @A.

+0

Có một bảng để xử lý không đủ http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1/1896 –

+0

Cũng xem xét sử dụng EXCEPT thay vì INTERSECT nếu mục tiêu cuối cùng của bạn là xóa chỉ từ một trong hai bảng được so sánh. Logic và các bước sẽ đơn giản hơn. –

+0

@MartinSmith - trong mẫu của bạn @A bị làm trống (đã xóa ...). Đúng là nếu mệnh đề 'FROM' trống, thì không có gì bị xóa, nhưng cũng giống như những gì đã xảy ra trong OP (trong đó mệnh đề' FROM' KHÔNG ĐƯỢC rỗng ...), lý do là như tôi đã nói. – Amit

0

Câu trả lời của Giorgi so sánh rõ ràng tất cả các cột mà bạn muốn tránh. Có thể viết mã không liệt kê tất cả các cột một cách rõ ràng. EXCEPT tạo tập hợp kết quả mà bạn cần, nhưng tôi không biết cách nào tốt để sử dụng kết quả này được đặt thành DELETE hàng gốc từ A không có khóa chính. Vì vậy, giải pháp dưới đây tiết kiệm kết quả trung gian này trong một bảng tạm thời sử dụng SELECT * INTO. Sau đó xóa tất cả mọi thứ từ A và sao chép kết quả tạm thời vào A. Quấn nó trong một giao dịch.

-- generate the final result set that we want to have and save it in a temporary table 
SELECT * 
INTO #t 
FROM 
(
    SELECT * FROM @A 
    EXCEPT 
    SELECT * FROM @B 
) AS E; 

-- copy temporary result back into A 
DELETE FROM @A; 

INSERT INTO @A 
SELECT * FROM #t; 

DROP TABLE #t; 

-- check the result 
SELECT * FROM @A; 

tập hợp kết quả

value username 
1  User 1 
3  User 3 

Các mặt tốt của giải pháp này là nó sử dụng * thay vì toàn bộ danh sách các cột. Tất nhiên, bạn có thể liệt kê tất cả các cột một cách rõ ràng là tốt.Nó sẽ vẫn dễ dàng hơn để viết và xử lý, hơn là viết so sánh của tất cả các cột và chăm sóc các NULL có thể.