2012-07-02 42 views
16

Tôi đang cố gắng xây dựng một câu lệnh SQL trả về các giá trị duy nhất, không null từ nhiều cột, tất cả đều nằm trong cùng một bảng.Chọn các giá trị riêng biệt từ nhiều cột trong cùng một bảng

SELECT distinct tbl_data.code_1 FROM tbl_data 
     WHERE tbl_data.code_1 is not null 
UNION 
SELECT tbl_data.code_2 FROM tbl_data 
     WHERE tbl_data.code_2 is not null; 

Ví dụ, tbl_data được như sau:

id code_1 code_2 
--- -------- ---------- 
1 AB  BC 
2 BC   
3 DE  EF 
4    BC 

Đối với bảng trên, các truy vấn SQL sẽ trả về tất cả các giá trị khác null độc đáo từ hai cột, cụ thể là: AB, BC, DE , EF.

Tôi khá mới đối với SQL. Tuyên bố của tôi ở trên hoạt động, nhưng có một cách sạch hơn để viết câu lệnh SQL này, vì các cột từ cùng một bảng?

+0

Cấu trúc bảng đó cho tôi cảm giác DB của bạn không được chuẩn hóa ... – gdoron

+4

Bạn không cần 'khác biệt' trong truy vấn đầu tiên -' union' sẽ làm điều đó cho bạn. – Blorgbeard

+0

@gdoron: Các mã tương ứng với các tên gọi khác nhau, có thể được lặp lại, tức là một bản ghi cụ thể có thể có BC và BC đối với mã 1 và 2. Chỉ định mã 1 so với 2 cũng đáng kể. Có một bảng tra cứu bảng thứ ba cho các mã khác nhau. Không phải là tốt nhất, nhưng đó là những gì tôi đang đối phó với. – regulus

Trả lời

18

Tốt hơn nên bao gồm mã câu hỏi của bạn, thay vì dữ liệu văn bản mơ hồ, để chúng tôi đều làm việc với cùng một dữ liệu. Dưới đây là lược đồ mẫu và dữ liệu tôi đã giả định:

CREATE TABLE tbl_data (
    id INT NOT NULL, 
    code_1 CHAR(2), 
    code_2 CHAR(2) 
); 

INSERT INTO tbl_data (
    id, 
    code_1, 
    code_2 
) 
VALUES 
    (1, 'AB', 'BC'), 
    (2, 'BC', NULL), 
    (3, 'DE', 'EF'), 
    (4, NULL, 'BC'); 

Như Blorgbeard nhận xét, các DISTINCT điều khoản trong giải pháp của bạn là không cần thiết vì các nhà điều hành UNION loại bỏ các hàng trùng lặp. Có một toán tử UNION ALL không loại bỏ trùng lặp, nhưng nó không thích hợp ở đây.

Viết lại câu hỏi của bạn mà không cần mệnh đề DISTINCT là một giải pháp tốt cho vấn đề này:

SELECT code_1 
FROM tbl_data 
WHERE code_1 IS NOT NULL 
UNION 
SELECT code_2 
FROM tbl_data 
WHERE code_2 IS NOT NULL; 

Nó không quan trọng mà hai cột là trong cùng một bảng. Giải pháp sẽ giống nhau ngay cả khi các cột nằm trong các bảng khác nhau.

Nếu bạn không thích những dư thừa của định mệnh đề bộ lọc tương tự hai lần, bạn có thể rút gọn các truy vấn liên minh trong một bảng ảo trước khi lọc rằng:

SELECT code 
FROM (
    SELECT code_1 
    FROM tbl_data 
    UNION 
    SELECT code_2 
    FROM tbl_data 
) AS DistinctCodes (code) 
WHERE code IS NOT NULL; 

tôi thấy cú pháp của thứ hai xấu xí hơn , nhưng nó là một cách logic. Nhưng cái nào hoạt động tốt hơn?

Tôi tạo ra một sqlfiddle đó chứng tỏ rằng tôi ưu truy vấn của SQL Server 2005 tạo ra kế hoạch thực hiện tương tự cho hai truy vấn khác nhau:

The query optimizer produces this execution plan for both queries: two table scans, a concatenation, a distinct sort, and a select.

Nếu SQL Server tạo ra các kế hoạch thực hiện tương tự cho hai truy vấn, thì chúng thực tế cũng như tương đương về mặt logic.

Hãy so sánh ở trên để kế hoạch thực hiện cho truy vấn trong câu hỏi của bạn:

The DISTINCT clause makes SQL Server 2005 perform a redundant sort operation.

Các DISTINCT khoản làm cho SQL Server 2005 thực hiện một hoạt động phân loại dư thừa, bởi vì tôi ưu truy vấn không biết rằng bất kỳ bản sao được lọc bởi DISTINCT trong truy vấn đầu tiên sẽ được lọc theo sau UNION sau đó.

Truy vấn này tương đương về mặt logic với hai phương pháp còn lại, nhưng thao tác dư thừa làm cho hoạt động kém hiệu quả hơn. Trên một tập dữ liệu lớn, tôi hy vọng truy vấn của bạn mất nhiều thời gian hơn để trả về một tập hợp kết quả hơn hai kết quả ở đây. Đừng dùng từ ngữ của tôi cho nó; thử nghiệm trong môi trường của riêng bạn để chắc chắn!

+0

Cảm ơn lời giải thích này! – regulus

+0

Một trong những câu trả lời hay nhất tôi nghĩ rằng tôi đã từng đọc trên SO. Chi tiết và ví dụ tuyệt vời và kết quả kiểm tra. – htm11h

4

thử một cái gì đó giống như SubQuery:

SELECT derivedtable.NewColumn 
FROM 
(
    SELECT code_1 as NewColumn FROM tbl_data 
    UNION 
    SELECT code_2 as NewColumn FROM tbl_data 
) derivedtable 
WHERE derivedtable.NewColumn IS NOT NULL 

Các UNION đã trả giá trị khác biệt từ các truy vấn kết hợp.

+0

Cảm ơn ví dụ này! – regulus

+0

Cảm ơn! Có vẻ như câu trả lời hoàn hảo tôi đang tìm kiếm! – NidhinSPradeep

Các vấn đề liên quan