Điều khoản CASE T-SQL: Cách chỉ định KHI NULL


227

Tôi đã viết một Tuyên bố T-SQL tương tự như thế này (bản gốc có vẻ khác nhưng tôi muốn đưa ra một ví dụ dễ hiểu ở đây):

SELECT first_name + 
    CASE last_name WHEN null THEN 'Max' ELSE 'Peter' END AS Name
FROM dbo.person

Tuyên bố này không có bất kỳ lỗi cú pháp nào nhưng mệnh đề trường hợp luôn chọn phần ELSE - cũng như nếu Last_name là null. Nhưng tại sao?

Điều tôi muốn làm là hợp nhất First_name và last_name, nhưng nếu last_name là null thì toàn bộ tên sẽ trở thành null:

SELECT first_name +
   CASE last_name WHEN null THEN '' ELSE ' ' + last_name END AS Name 
FROM dbo.person

Bạn có biết vấn đề ở đâu không?

Câu trả lời:


369
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

4
Đề nghị COALESCE của Luther tốt hơn câu trả lời của tôi. Nó kém hiệu quả hơn một chút, nhưng thanh lịch hơn nhiều.
Marcelo Cantos

Xử lý nó với một cái gì đó như thế này có thể được sử dụng, hoặc một cái gì đó tương tự: COALESCE (last_name, '') Câu lệnh tình huống, trong khi súc tích, là imho ít có thể duy trì hơn COALESCE trong các truy vấn lớn. Cuối cùng, bạn có kết quả tương tự. Nếu bạn cần tối ưu hóa, hãy kiểm tra các kế hoạch thực hiện nhưng tôi không nhận thấy nhiều sự khác biệt.
Anthony Mason

1
COALESCEvề cơ bản nội bộ dịch vào CASE. Vấn đề với CASE là last_nameđược đánh giá hai lần và nó có thể dẫn đến các tác dụng phụ khác - xem bài viết tuyệt vời này . Để giải quyết những gì đã được hỏi, tôi muốn đi với ISNULL(' '+ last_name, '')đề cập trong bình luận dưới đây.
miroxlav

41

Phần WHEN được so sánh với ==, nhưng bạn thực sự không thể so sánh với NULL. Thử

CASE WHEN last_name is NULL  THEN ... ELSE .. END

thay vào đó hoặc COALESCE:

COALESCE(' '+last_name,'')

('' + last_name là NULL khi last_name là NULL, vì vậy nó sẽ trả về '' trong trường hợp đó)


Được rồi cảm ơn bạn đã thông tin với == trong phần KHI. Tôi không biết điều đó. Với COALESCE nó cũng hoạt động tốt. Haven't nghĩ rằng có rất nhiều khả năng để làm điều đó.
meni

15

Có rất nhiều giải pháp nhưng không có giải pháp nào giải thích tại sao tuyên bố ban đầu không hoạt động.

CASE last_name WHEN null THEN '' ELSE ' '+last_name

Sau khi, có một kiểm tra cho sự bình đẳng, nên đúng hoặc sai.

Nếu một hoặc cả hai phần của một so sánh là null, kết quả của so sánh sẽ là UNKNOWN, được coi là sai trong cấu trúc trường hợp. Xem: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

Để tránh điều này, Coalesce là cách tốt nhất.


11

Đưa ra truy vấn của bạn, bạn cũng có thể làm điều này:

SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person

2
Điều này thêm một không gian dư thừa khi last_name là null, đó là điều mà OP đang cố tránh.
Marcelo Cantos

6
Sử dụng tốt hơn ISNULL(' '+ last_name, '')để ngăn chặn không gian dư thừa.
Prutswonder

Vâng nhận ra nó sau khi tôi đăng. Hình tôi sẽ rời khỏi nó vì nó đã giải quyết được phần mà anh ấy gặp khó khăn.
Ian Jacobs

7

Vấn đề là null không được coi là bằng chính nó, do đó mệnh đề không bao giờ khớp.

Bạn cần kiểm tra null rõ ràng:

SELECT CASE WHEN last_name is NULL THEN first_name ELSE first_name + ' ' + last_name

5

thử:

SELECT first_name + ISNULL(' '+last_name, '') AS Name FROM dbo.person

Điều này thêm không gian vào tên cuối cùng, nếu nó là null, toàn bộ dấu cách + họ sẽ thuộc về NULL và bạn chỉ nhận được một tên, nếu không, bạn sẽ nhận được một dấu phẩy + dấu cách + họ.

điều này sẽ hoạt động miễn là cài đặt mặc định cho nối với chuỗi null được đặt:

SET CONCAT_NULL_YIELDS_NULL ON 

đây không phải là vấn đề đáng lo ngại vì OFFchế độ sẽ biến mất trong các phiên bản tương lai của Máy chủ SQl


4

Vấn đề là NULL không được coi là tương đương với bất cứ thứ gì thậm chí không phải với chính nó, nhưng phần lạ là nó cũng không bằng chính nó.

Hãy xem xét các câu lệnh sau (BTW là bất hợp pháp trong SQL Server T-SQL nhưng hợp lệ trong My-SQL, tuy nhiên đây là những gì ANSI định nghĩa cho null và có thể được xác minh ngay cả trong SQL Server bằng cách sử dụng các câu lệnh tình huống, v.v.)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

Vì vậy, không có câu trả lời đúng / sai cho câu hỏi, thay vào đó câu trả lời cũng là null.

Điều này có nhiều ý nghĩa, ví dụ như trong

  1. Các câu lệnh CASE, trong đó mọi giá trị null sẽ luôn sử dụng mệnh đề ELSE trừ khi bạn sử dụng rõ ràng điều kiện WHEN IS NULL ( KHÔNG phảiWHEN NULLđiều kiện )
  2. Nối chuỗi, như
    SELECT a + NULL -- Results in NULL
  3. Trong mệnh đề WHERE IN hoặc WHERE NOT IN, như thể bạn muốn có kết quả chính xác, hãy đảm bảo trong truy vấn phụ tương quan để lọc ra bất kỳ giá trị null nào.

Người ta có thể ghi đè hành vi này trong SQL Server bằng cách chỉ định SET ANSI_NULLS OFF, tuy nhiên điều này KHÔNG được khuyến nghị và không nên được thực hiện vì nó có thể gây ra nhiều vấn đề, đơn giản là vì độ lệch của tiêu chuẩn.

(Như một lưu ý phụ, trong My-SQL có một tùy chọn sử dụng toán tử đặc biệt <=>để so sánh null.)

Trong so sánh, trong các ngôn ngữ lập trình chung, null được coi là một giá trị thông thường và bằng chính nó, tuy nhiên giá trị NAN cũng không bằng chính nó, nhưng ít nhất nó trả về 'false' khi so sánh nó với chính nó, (và khi kiểm tra không bằng các ngôn ngữ lập trình khác nhau có các cách triển khai khác nhau).

Tuy nhiên, xin lưu ý rằng trong các ngôn ngữ Cơ bản (ví dụ VB, v.v.) không có từ khóa 'null' và thay vào đó, người ta sử dụng từ khóa 'Không có gì', không thể sử dụng để so sánh trực tiếp và thay vào đó, người ta cần sử dụng 'IS' như trong SQL, tuy nhiên trên thực tế nó bằng chính nó (khi sử dụng so sánh gián tiếp).


1
"Phần kỳ lạ là nó cũng không bằng chính nó." => điều này không có gì lạ khi xem xét null trong SQL có nghĩa là 'không xác định'. Một cái gì đó chưa biết, rất có thể không giống như một cái gì đó không rõ ràng.
JDC

1
CASE  
    WHEN last_name IS null THEN '' 
    ELSE ' ' + last_name 
END

1

Khi bạn thất vọng khi thử điều này:

CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

Hãy thử cái này thay thế:

CASE LEN(ISNULL(last_Name,''))
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

LEN(ISNULL(last_Name,''))đo số lượng ký tự trong cột đó, bằng 0 cho dù nó trống hay NULL, do đó WHEN 0 THENsẽ đánh giá là đúng và trả về '' như mong đợi.

Tôi hy vọng đây là một sự thay thế hữu ích.

Tôi đã bao gồm trường hợp thử nghiệm này cho máy chủ sql 2008 trở lên:

DECLARE @last_Name varchar(50) = NULL

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

SET @last_Name = 'LastName'

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

2
Bạn có chắc chắn rằng nó hoạt động? Hầu hết các hàm trả về NULL khi được cung cấp đầu vào NULL và các thử nghiệm của tôi xác nhận rằng điều đó cũng đúng với LEN () tức là LEN (NULL) trả về NULL chứ không phải 0.
Jason Musgrove

Pokechu22 là đúng, và đây là kịch bản điều chỉnh: CASE LEN (ISNULL (last_name, '')) KHI 0 THEN '' ELSE '' + END last_name AS newlastName
Chef Slagle

0

Jason đã bắt gặp một lỗi, vì vậy điều này hoạt động ...

Bất cứ ai cũng có thể xác nhận các phiên bản nền tảng khác?
Máy chủ SQL:

SELECT
CASE LEN(ISNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

MySQL:

SELECT
CASE LENGTH(IFNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

Oracle:

SELECT
CASE LENGTH(NVL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

0

Bạn có thể sử dụng hàm IsNull

select 
    isnull(rtrim(ltrim([FirstName]))+' ','') +
    isnull(rtrim(ltrim([SecondName]))+' ','') +
    isnull(rtrim(ltrim([Surname]))+' ','') +
    isnull(rtrim(ltrim([SecondSurname])),'')
from TableDat

Nếu một cột là null, bạn sẽ nhận được một char trống

Tương thích với Microsoft SQL Server 2008+


0

Sử dụng chức năng CONCAT có sẵn trong SQL Server 2012 trở đi.

SELECT CONCAT([FirstName], ' , ' , [LastName]) FROM YOURTABLE

0

Tôi đã thử truyền vào một chuỗi và kiểm tra chuỗi có độ dài bằng không và nó đã hoạt động.

CASE 
   WHEN LEN(CAST(field_value AS VARCHAR(MAX))) = 0 THEN 
       DO THIS
END AS field 

0

NULL không bằng bất cứ điều gì. Các tuyên bố trường hợp về cơ bản là nói khi giá trị = NULL .. nó sẽ không bao giờ đạt được.
Ngoài ra còn có một số thủ tục lưu trữ hệ thống được viết không đúng với cú pháp của bạn. Xem sp_addpullsubcrip_agent và sp_who2.
Ước gì tôi biết cách thông báo cho Microsoft về những sai lầm đó vì tôi không thể thay đổi hệ thống lưu trữ procs.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.