2012-01-05 25 views
6

Tôi cần trả lại tất cả các giá trị từ colA không nằm trong số colB từ mytable. Tôi đang sử dụng:Cách hiệu quả để chọn tất cả các giá trị từ một cột không nằm trong cột khác

SELECT DISTINCT(colA) FROM mytable WHERE colA NOT IN (SELECT colB FROM mytable) 

Vẫn đang hoạt động, truy vấn mất quá nhiều thời gian để hoàn thành.

Có cách nào hiệu quả hơn để thực hiện việc này không?

+0

'NOT IN' chậm lại khi kích thước thiết lập tăng lên và thường có giới hạn về số lượng hàng có thể nằm trong mệnh đề' NOT IN'. Bên ngoài các bộ kết quả nhỏ, tôi đã tìm thấy nó tốt hơn để sử dụng các phương tiện khác để có được sự khác biệt giữa hai bộ kết quả. – Paul

+0

Khi nói về hiệu suất, bạn phải đặt tên cho RDBMS của bạn hoặc nhận được câu trả lời tối ưu. –

Trả lời

10

Trong SQL chuẩn có không có dấu ngoặc đơn trong DISTINCT colA. DISTINCT không phải là chức năng.

SELECT DISTINCT colA 
FROM mytable 
WHERE colA NOT IN (SELECT DISTINCT colB FROM mytable); 

Đã thêm DISTINCT vào phụ chọn. Nếu bạn có nhiều bản sao, nó có thể tăng tốc truy vấn.

CTE có thể nhanh hơn, tùy thuộc vào DBMS của bạn. Tôi bổ sung chứng minh LEFT JOIN như thay thế để loại trừ các giá trị trong valB, và một cách khác để có được giá trị khác biệt với GROUP BY:

WITH x AS (SELECT colB FROM mytable GROUP BY colB) 
SELECT m.colA 
FROM mytable m 
LEFT JOIN x ON x.colB = m.colA 
WHERE x.colB IS NULL 
GROUP BY m.colA; 

Hoặc đơn giản hơn nữa, và với một subquery đồng bằng (có thể là nhanh nhất):

SELECT DISTINCT m.colA 
FROM mytable m 
LEFT JOIN mytable x ON x.colB = m.colA 
WHERE x.colB IS NULL; 

cơ bản 4 kỹ thuật để loại trừ hàng với các phím xuất hiện trong một (hoặc tương tự) bảng:

Các yếu tố quyết định cho tốc độ sẽ chỉ số. Bạn cần có chỉ mục trên colAcolB để truy vấn này diễn ra nhanh chóng.

+0

Cảm ơn, tôi đã thử: 'CHỌN DISTINCT m1.colA TỪ mytable m1 LEFT THAM GIA mytable m2 ON (m1.colA = m2.colB) WHERE m2.colA IS NULL ORDER BY m1.colA ASC' và nó có một số đơn đặt hàng có cường độ nhanh hơn và dường như đang hoạt động - điều này có tương đương với mã tôi đã đăng trong câu hỏi không? Nó nhanh hơn rất nhiều đến mức tôi nghi ngờ rằng tôi có thể đã bỏ lỡ điều gì đó. – Flash

+0

@Andrew: Rất tiếc, bạn đã nhận được lỗi đánh máy trong truy vấn của mình. Phải là 'WHERE m2.colB IS NULL'. Truy vấn (đã sửa) có thể nhanh hơn với 'LEFT JOIN (SELECT DISTINCT colB FROM mytable) m2 ON m2.colB = m1.colA' ** nếu ** có nhiều giá trị trùng lặp cho' colB'. –

+0

@Andrew: 'm2.colA' luôn là' NULL' nếu 'm2.colB' là NULL ở đây, nhưng' m2.colA' có thể là NULL ngay cả khi 'm2.colB' không phải là. Vì vậy, đúng (và nhanh hơn!) Hình thức ở đây là: 'WHERE m2.colB IS NULL'. ** Nếu ** 'colA' được định nghĩa NOT NULL, thì truy vấn trên của bạn là đúng. –

6

Bạn có thể sử dụng exists:

select distinct 
    colA 
from 
    mytable m1 
where 
    not exists (select 1 from mytable m2 where m2.colB = m1.colA) 

exists hiện một bán tham gia để nhanh chóng phù hợp với các giá trị. not in hoàn thành toàn bộ tập hợp kết quả và sau đó thực hiện or trên đó. exists thường nhanh hơn cho các giá trị trong bảng.

+0

Bạn có thể giải thích cách truy vấn này hoạt động không? – Flash

+0

@Andrew - Chắc chắn! Nó nói, lấy 'colA' riêng biệt, không có hàng nào từ 'mytable' mà' colB' bằng với 'colA'. – Eric

0

Bạn có thể sử dụng toán tử EXCEPT có hiệu quả khác với hai truy vấn SELECT. EXCEPT DISTINCT sẽ chỉ trả lại giá trị duy nhất. Nhà điều hành MINUS của Oracle tương đương với EXCEPT DISTINCT.