2011-12-21 35 views
9

Tôi đã có một bộ nhớ mơ hồ, có thể là hàng hóa-sùng bái từ nhiều năm làm việc với SQL Server khi bạn đã có một cột có thể null, không an toàn để viết mệnh đề "WHERE" vị từ như:Toán tử SQL và logic và kiểm tra null

... WHERE the_column IS NULL OR the_column < 10 ... 

Nó có cái gì để làm với thực tế rằng những quy tắc SQL không quy định ngắn mạch (và trong thực tế đó là loại của một ý tưởng tồi có thể vì lý do tối ưu hóa truy vấn), và do đó " < "so sánh (hoặc bất kỳ) có thể được đánh giá ngay cả khi giá trị cột là null. Bây giờ, chính xác lý do tại sao đó sẽ là một điều khủng khiếp, tôi không biết, nhưng tôi nhớ lại được một cách nghiêm khắc cảnh báo bởi một số tài liệu để luôn mã mà như là một "Case" khoản:

... WHERE 1 = CASE WHEN the_column IS NULL THEN 1 WHEN the_column < 10 THEN 1 ELSE 0 END ... 

(những ngốc nghếch "1 =" phần là bởi vì SQL server không/không có phép toán hạng nhất, hoặc ít nhất tôi nghĩ rằng nó không)

vì vậy, câu hỏi của tôi ở đây là:.

  1. is điều đó thực sự đúng đối với SQL Server (hoặc có lẽ là back-rev SQL Server 2000 hoặc 2005) hay tôi chỉ là các loại hạt?
  2. Nếu có, hãy áp dụng cùng một caveat cho PostgreSQL? (8.4 nếu nó quan trọng)
  3. Chính xác thì vấn đề là gì? Nó có liên quan đến cách các chỉ mục hoạt động hay không?

Nền tảng trong SQL của tôi khá yếu.

+1

Có lẽ họ đang nói về AND? Vì null và bất kỳ thứ gì là null, kết hợp lại hoặc một trường hợp thường là cần thiết khi các biểu thức có thể chứa các thuật ngữ rỗng. –

Trả lời

10

Tôi không biết SQL Server vì vậy tôi không thể nói điều đó.

Với một biểu a L b đối với một số toán tử logic L, không có gì bảo đảm rằng a sẽ được đánh giá trước hoặc sau b hoặc thậm chí là cả ab sẽ được đánh giá:

Expression Evaluation Rules

Các thứ tự đánh giá các biểu thức con không được xác định. Đặc biệt, đầu vào của một toán tử hoặc hàm không nhất thiết được đánh giá từ trái sang phải hoặc theo bất kỳ thứ tự cố định nào khác.

Hơn nữa, nếu kết quả của một biểu thức có thể được xác định bằng cách đánh giá chỉ một số phần của nó, thì các biểu thức con khác có thể không được đánh giá.
[...]
Lưu ý rằng điều này không giống như "short-circuiting" từ trái sang phải của các toán tử Boolean được tìm thấy trong một số ngôn ngữ lập trình.

Kết quả là, không sử dụng các hàm có tác dụng phụ như là một phần của biểu thức phức tạp. Đặc biệt nguy hiểm khi dựa vào các tác dụng phụ hoặc thứ tự đánh giá trong các mệnh đề WHEREHAVING vì các mệnh đề đó được tái chế rộng rãi như là một phần của việc phát triển kế hoạch thực hiện.

Theo như một biểu thức có dạng:

the_column IS NULL OR the_column < 10 

là có liên quan, không có gì phải lo lắng về từ NULL < nNULL cho tất cả n, thậm chí NULL < NULL đánh giá để NULL; Hơn nữa, NULL là không đúng sự thật nên

null is null or null < 10 

chỉ là một cách phức tạp nói true or null và đó là true bất kể là tiểu biểu được đánh giá đầu tiên.

Toàn bộ "sử dụng CASE" âm thanh chủ yếu giống như SQL lồng ghép hàng hóa với tôi. Tuy nhiên, giống như hầu hết hàng hóa-văn hóa, có một hạt nhân một sự thật bị chôn vùi dưới hàng hóa; ngay dưới đoạn trích đầu tiên của tôi từ hướng dẫn sử dụng PostgreSQL, bạn sẽ thấy điều này:

Khi cần thiết phải thực hiện lệnh đánh giá, có thể sử dụng cấu trúc CASE (xem Mục 9.16). Ví dụ, đây là một cách không đáng tin cậy của cố gắng để tránh phép chia cho không trong một điều khoản WHERE:

SELECT ... WHERE x > 0 AND y/x > 1.5; 

Nhưng đây là an toàn:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END; 

Vì vậy, nếu bạn cần để bảo vệ chống lại một điều kiện sẽ tăng ngoại lệ hoặc có các tác dụng phụ khác, khi đó bạn nên sử dụng số CASE để kiểm soát thứ tự đánh giá dưới dạng là evaluated in order:

Mỗi điều kiện là biểu thức trả về kết quả boolean. Nếu kết quả của điều kiện là đúng, giá trị của biểu thức CASEkết quả tuân theo điều kiện và phần còn lại của biểu thức CASE không được xử lý. Nếu kết quả của điều kiện không đúng, thì bất kỳ mệnh đề WHEN tiếp theo nào cũng được kiểm tra theo cách tương tự.

Vì vậy, cho này:

case when A then Ra 
    when B then Rb 
    when C then Rc 
    ... 

A được đảm bảo để được đánh giá trước B, B trước C, vv và thẩm định dừng lại trong thời gian sớm là một trong những điều kiện để đánh giá một giá trị true.

Nói tóm lại, một CASE ngắn mạch buts không AND cũng không OR ngắn mạch, do đó bạn chỉ cần sử dụng một CASE khi bạn cần để bảo vệ chống lại tác dụng phụ.

+1

Có cảm ơn; Tôi hiểu thực tế là SQL không thực thi một quy tắc ngắn mạch (hoặc, cách khác, một "không ngắn mạch"). Câu hỏi đặt ra thực sự là liệu có điều gì đó khủng khiếp xảy ra nếu so sánh quan hệ bình thường được đánh giá dựa vào cột có thể null. Cảm ơn bạn đã trả lời rất chi tiết. – Pointy

1

Tôi chưa bao giờ nghe nói về một vấn đề như vậy, và this bit of SQL Server 2000 documentation sử dụng WHERE advance < $5000 OR advance IS NULL trong một ví dụ, do đó, nó không phải là một quy tắc rất nghiêm khắc. Mối quan tâm duy nhất của tôi với OR là mức độ ưu tiên thấp hơn AND, vì vậy bạn có thể vô tình viết một cái gì đó như WHERE the_column IS NULL OR the_column < 10 AND the_other_column > 20 khi đó không phải ý của bạn; nhưng giải pháp thông thường là dấu ngoặc đơn chứ không phải là biểu thức lớn CASE.

Tôi nghĩ rằng trong hầu hết các RDBMS, chỉ mục không bao gồm giá trị null, vì vậy chỉ mục trên the_column sẽ không hữu ích cho truy vấn này; nhưng ngay cả khi đó không phải là trường hợp, tôi không thấy lý do tại sao một biểu thức lớn CASE sẽ trở nên thân thiện với chỉ mục hơn.

(Tất nhiên, thật khó để chứng minh một tiêu cực, và có lẽ người khác sẽ biết những gì bạn đang đề cập đến?)

1

Vâng, tôi đã viết nhiều lần các truy vấn như ví dụ đầu tiên kể từ khoảng mãi mãi (heck, tôi đã viết các trình tạo truy vấn tạo các truy vấn như vậy) và tôi chưa bao giờ gặp sự cố.

Tôi nghĩ bạn có thể đang nhớ một số lời khuyên mà ai đó đã cho bạn đôi khi chống lại việc viết funky tham gia các điều kiện sử dụng OR. Trong ví dụ đầu tiên của bạn, các điều kiện được nối bởi OR hạn chế cùng một cột của cùng một bảng, đó là OK. Nếu điều kiện thứ hai của bạn là một điều kiện kết nối (nghĩa là nó hạn chế các cột từ hai bảng khác nhau), thì bạn có thể gặp phải các tình huống xấu mà người lập kế hoạch truy vấn không có lựa chọn nào khác ngoài sử dụng tham gia Descartes (xấu, xấu, xấu !!!).

Tôi không nghĩ rằng chức năng CASE của bạn thực sự đang làm bất kỳ điều gì ở đó, ngoại trừ có thể cản trở các nỗ lực của người lập kế hoạch truy vấn trong việc tìm kiếm một kế hoạch thực hiện tốt cho truy vấn.

Nhưng nói chung, chỉ cần viết truy vấn đơn giản trước và xem cách nó hoạt động cho dữ liệu thực tế. Không cần phải lo lắng về một vấn đề có thể thậm chí không tồn tại!

0

Nulls có thể gây nhầm lẫn. "... WHERE 1 = CASE ..." rất hữu ích nếu bạn đang cố chuyển một giá trị Null HOẶC một giá trị như một tham số cũ. "Ở ĐÂU the_column = @parameter. Bài viết này có thể hữu ích Passing Null using OLEDB.

1

Thay vì

the_column IS NULL OR the_column < 10 

Tôi muốn làm

isnull(the_column,0) < 10 

hoặc cho ví dụ đầu tiên

WHERE 1 = CASE WHEN isnull(the_column,0) < 10 THEN 1 ELSE 0 END ... 
0

Một ví dụ khác mà CASE hữu ích là khi sử dụng các hàm ngày tháng trên các cột VARCHAR, thêm ISDATE trước khi bạn hát nói chuyển đổi (colA, datetime) có thể không hoạt động, và khi colA có dữ liệu không phải ngày, truy vấn có thể bị lỗi.

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