2012-05-02 33 views

Trả lời

34

Vị từ IS DISTINCT FROM được giới thiệu là tính năng T151 của SQL: 1999 và từ chối đọc được của nó, IS NOT DISTINCT FROM, được thêm vào làm T152 tính năng của SQL: 2003. Mục đích của các vị từ này là để đảm bảo rằng kết quả của việc so sánh hai giá trị là Đúng hoặc False, không bao giờ Không xác định.

Các biến vị ngữ này hoạt động với bất kỳ loại so sánh nào (bao gồm hàng, mảng và multisets) làm cho nó khá phức tạp để mô phỏng chính xác. Tuy nhiên, SQL Server không hỗ trợ hầu hết các loại, vì vậy chúng tôi có thể nhận được khá xa bằng cách kiểm tra cho các đối số null/toán hạng:

  • a IS DISTINCT FROM b thể được viết lại như sau:

    ((a <> b OR a IS NULL OR b IS NULL) AND NOT (a IS NULL AND b IS NULL)) 
    
  • a IS NOT DISTINCT FROM b thể được viết lại như sau:

    (NOT (a <> b OR a IS NULL OR b IS NULL) OR (a IS NULL AND b IS NULL)) 
    

Câu trả lời của riêng bạn không chính xác vì không xem xét rằng FALSE OR NULL đánh giá là Không xác định. Ví dụ: NULL IS DISTINCT FROM NULL phải đánh giá là False. Tương tự, 1 IS NOT DISTINCT FROM NULL phải đánh giá là False.Trong cả hai trường hợp, biểu thức của bạn mang lại Không xác định.

+0

((a <> b HOẶC một IS NULL HOẶC b IS NULL) VÀ NOT (một IS NULL và b IS NULL)): không thể chúng ta chỉ là một <> b hoặc a là null xor b là null –

12

Nếu thực hiện SQL của bạn không thực hiện các tiêu chuẩn SQL IS DISTINCT FROMIS NOT DISTINCT FROM nhà khai thác, bạn có thể viết lại biểu thức chứa chúng bằng cách sử dụng equivalencies sau:

Nói chung:

a IS DISTINCT FROM b <==> 
(
    ((a) IS NULL AND (b) IS NOT NULL) 
OR 
    ((a) IS NOT NULL AND (b) IS NULL) 
OR 
    ((a) <> (b)) 
) 

a IS NOT DISTINCT FROM b <==> 
(
    ((a) IS NULL AND (b) IS NULL) 
OR 
    ((a) = (b)) 
) 

Câu trả lời này là không chính xác khi sử dụng trong một ngữ cảnh có sự khác biệt giữa các vấn đề KHÔNG GIỚI HẠN và FALSE. Mặc dù vậy, tôi nghĩ điều đó là không phổ biến. Xem câu trả lời được chấp nhận bởi @ChrisBandy.

Nếu một giá trị giữ chỗ có thể được xác định mà không thực sự xảy ra trong dữ liệu, sau đó COALESCE là một thay thế:

a IS DISTINCT FROM b <==> COALESCE(a, placeholder) <> COALESCE(b, placeholder) 
a IS NOT DISTINCT FROM b <==> COALESCE(a, placeholder) = COALESCE(b, placeholder) 
+5

Đây là một câu trả lời sai mặc dù. Xem đoạn cuối trong câu trả lời của Chris. –

+1

Có, câu trả lời này không chính xác khi được sử dụng trong bối cảnh có sự khác biệt giữa các vấn đề KHÔNG BAO GIỜ và FALSE. Mặc dù vậy, tôi nghĩ điều đó là không phổ biến. –

+3

@JasonKresowaty: Điều này không phải là hiếm. Trong bất kỳ biến vị ngữ nào như '(một dấu cách riêng biệt b) VÀ cái gì đó', sự khác biệt giữa' UNKNOWN' và 'FALSE' là điều cần thiết. Nếu 'a' và' b' là cả hai 'NULL', thì giả lập của bạn sẽ tạo ra' NULL', bất kể 'something' là' TRUE' hay 'FALSE'. –

4

Một caveat trong viết lại là khác biệt với và IS NOT DISTINCT TỪ sẽ không can thiệp với việc sử dụng các chỉ mục, ít nhất là khi sử dụng SQL Server. Nói cách khác, khi sử dụng như sau:

WHERE COALESCE(@input, x) = COALESCE(column, x) 

SQL Server sẽ không thể sử dụng bất kỳ chỉ số bao gồm cột. Vì vậy, trong một mệnh đề WHERE, nó sẽ là thích hợp hơn để sử dụng mẫu

WHERE @input = column OR (@input IS NULL AND column IS NULL) 

để tận dụng lợi thế của bất kỳ chỉ số cho cột. (Parens chỉ được sử dụng để rõ ràng)

+0

+1 để đề cập đến cách các hàm giết sử dụng của chỉ mục. Đó là cách tôi kết thúc ở đây ngay từ đầu. – Serge

18

Một giải pháp khác tôi thích tận dụng kết quả boolean hai giá trị thực của EXISTS kết hợp với INTERSECT. Giải pháp này sẽ hoạt động trong SQL Server 2005+.

  • a IS NOT DISTINCT FROM b có thể được viết như sau:

    EXISTS(SELECT a INTERSECT SELECT b)

Như ghi nhận, INTERSECT đối xử với hai giá trị NULL như bình đẳng, vì vậy nếu cả hai được NULL, sau đó INTERSECT kết quả trong một hàng duy nhất, do đó EXISTS mang lại sự thật.

  • a IS DISTINCT FROM b có thể được viết như sau:

    NOT EXISTS(SELECT a INTERSECT SELECT b)

Cách tiếp cận này là nhiều hơn nữa ngắn gọn nếu bạn có nhiều cột nullable bạn cần phải so sánh trong hai bảng. Ví dụ, để trả lại hàng trong TableB có giá trị khác nhau cho col1, col2, hoặc Col3 hơn TableA, sau đây có thể được sử dụng:

SELECT * 
FROM TableA A 
    INNER JOIN TableB B ON A.PK = B.PK 
WHERE NOT EXISTS(
    SELECT A.Col1, A.Col2, A.Col3 
    INTERSECT 
    SELECT B.Col1, B.Col2, B.Col3); 

Paul White giải thích cách giải quyết này một cách chi tiết hơn: http://sqlblog.com/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx

+3

Điều này cần được chấp nhận câu trả lời, vì nó viết lại vị từ theo cách không trùng lặp các tham chiếu đến 'a' và' b'. Đối với các biểu thức không xác định 'a' và' b', hoặc các biểu thức với các hiệu ứng phụ (chẳng hạn như ghi nhật ký), điều đó sẽ rất hữu ích. Ví dụ thứ hai của bạn cũng mô phỏng '(A.Col1, A.Col2, A.Col3) LÀ CHỮ KÝ TỪ (B.Col1, B.Col2, B.Col3)', chỉ được hỗ trợ bởi PostgreSQL (theo kiến ​​thức của tôi). Một vị ngữ rất hữu ích, đôi khi. –

+0

Câu trả lời hay, tôi thích cái này hơn cái được chấp nhận – Jocie

1

Để tham khảo, việc triển khai thực hiện theo quy tắc kinh điển nhất (và có thể đọc) là IS [ NOT ] DISTINCT FROM sẽ là một biểu thức được định dạng tốt CASE. Đối với IS DISTINCT FROM:

CASE WHEN [a] IS  NULL AND [b] IS  NULL THEN FALSE 
    WHEN [a] IS  NULL AND [b] IS NOT NULL THEN TRUE 
    WHEN [a] IS NOT NULL AND [b] IS  NULL THEN TRUE 
    WHEN [a] =    [b]    THEN FALSE 
    ELSE           TRUE 
END 

Rõ ràng, giải pháp khác (cụ thể là John Keller's, sử dụng INTERSECT) là ngắn gọn hơn.

More details here

0

Các biểu thức này có thể là một thay thế tốt cho khác với logic và thực hiện tốt hơn so với ví dụ trước vì họ kết thúc được biên soạn bởi SQL server vào một biểu ngữ duy nhất mà sẽ cho kết quả xấp xỉ. một nửa chi phí của nhà khai thác trên biểu thức lọc. Về cơ bản, chúng giống như các giải pháp được cung cấp bởi Chris Bandy, tuy nhiên chúng sử dụng các hàm ISNULL và NULLIF lồng nhau để thực hiện các so sánh bên dưới.

(...rõ ràng ISNULL có thể được thay thế bằng liên hiệp nếu bạn thích)

  • a IS DISTINCT FROM b thể được viết lại như sau:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NOT NULL

  • a IS NOT DISTINCT FROM b thể được viết lại như sau:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NULL

0
a IS NOT DISTINCT FROM b 

thể được viết lại như sau:

(a IS NOT NULL AND b IS NOT NULL AND a=b) OR (a IS NULL AND b is NULL) 

a IS DISTINCT FROM b 

thể được viết lại như sau:

NOT (a IS NOT DISTINCT FROM b)