SQL Server 2008 Chuỗi trống so với Không gian


82

Tôi đã gặp phải một điều gì đó hơi kỳ lạ vào sáng nay và nghĩ rằng tôi sẽ gửi nó để bình luận.

Ai đó có thể giải thích tại sao truy vấn SQL sau in 'bằng' khi chạy với SQL 2008. Mức độ tương thích db được đặt thành 100.

if '' = ' '
    print 'equal'
else
    print 'not equal'

Và điều này trả về 0:

select (LEN(' '))

Có vẻ như nó đang tự động cắt bớt không gian. Tôi không biết liệu đây có phải là trường hợp của các phiên bản SQL Server trước hay không và tôi không còn có thể kiểm tra nó nữa.

Tôi gặp phải vấn đề này vì truy vấn sản xuất trả về kết quả không chính xác. Tôi không thể tìm thấy hành vi này được ghi lại ở bất cứ đâu.

Có ai biết bất kỳ thông tin gì về điều này ko?


2
SQL 2005: select len ​​('') trả về 0
Mayo

1
Nó hoạt động tương tự trên Sql Server 2000.
Pierre-Alain Vigeant

1
Đây là một câu hỏi hấp dẫn. Nó dường như trả về bằng nhau bất kể bạn đặt bao nhiêu dấu cách vào một trong hai chuỗi cho dù chúng có khớp hay không. Sau nhiều thử nghiệm hơn, tôi nhận thấy rằng nó đang thực hiện một cách hiệu quả RTRIM ở cả hai phía của toán tử bình đẳng trước khi so sánh. Có vẻ như bạn đã nhận được câu trả lời về hàm LEN, nhưng tôi thực sự quan tâm đến một câu trả lời thấu đáo hơn là "varchars và bình đẳng là điều khó khăn trong TSQ" cho phần bình đẳng trong câu hỏi của bạn.
JohnFx

Tôi tin là Oracle cũng làm được điều này.
quillbreaker 09/09/09

Nói chung, tôi thấy rằng lưu trữ chuỗi rỗng là một ý tưởng tồi và đây là một trong những lý do. Tôi thích sử dụng Null hơn và nhận thấy nhiều vấn đề khi mọi người cố gắng biến thông tin null thành một giá trị như chuỗi rỗng hoặc cách dữ liệu nằm ngoài phạm vi bình thường.
HLGEM

Câu trả lời:


87

varchars và bình đẳng là điều khó khăn trong TSQL. Các LENchức năng cho biết:

Trả về số ký tự, thay vì số byte, của biểu thức chuỗi đã cho, không bao gồm khoảng trống ở cuối .

Bạn cần sử dụng DATALENGTHđể có được bytesố lượng dữ liệu thực sự được đề cập. Nếu bạn có dữ liệu unicode, hãy lưu ý rằng giá trị bạn nhận được trong trường hợp này sẽ không giống với độ dài của văn bản.

print(DATALENGTH(' ')) --1
print(LEN(' '))        --0

Khi nói đến sự bình đẳng của các biểu thức, hai chuỗi được so sánh để bình đẳng như sau:

  • Nhận chuỗi ngắn hơn
  • Đệm với khoảng trống cho đến khi độ dài bằng với độ dài của chuỗi dài hơn
  • So sánh hai

Đó là bước giữa gây ra kết quả không mong đợi - sau bước đó, bạn đang so sánh hiệu quả khoảng trắng với khoảng trắng - do đó chúng được coi là bằng nhau.

LIKEhoạt động tốt hơn =trong tình huống "khoảng trống" vì nó không thực hiện đệm trống trên mẫu mà bạn đang cố gắng đối sánh:

if '' = ' '
print 'eq'
else
print 'ne'

Sẽ đưa ra eqtrong khi:

if '' LIKE ' '
print 'eq'
else
print 'ne'

Sẽ cho ne

Cẩn thận với LIKEmặc dù: nó không đối xứng: nó coi khoảng trắng ở cuối là quan trọng trong mẫu (RHS) nhưng không phải là biểu thức so khớp (LHS). Phần sau được lấy từ đây :

declare @Space nvarchar(10)
declare @Space2 nvarchar(10)

set @Space = ''
set @Space2 = ' '

if @Space like @Space2
print '@Space Like @Space2'
else
print '@Space Not Like @Space2'

if @Space2 like @Space
print '@Space2 Like @Space'
else
print '@Space2 Not Like @Space'

@Space Not Like @Space2
@Space2 Like @Space

1
Câu trả lời hay đấy. Tôi đã không nhận thấy điều đó trong tài liệu LEN. Tuy nhiên, nó không giới hạn ở LEN. Hàm RIGHT và LEFT thể hiện hành vi tương tự, nhưng nó không được ghi lại. Nó dường như là nghĩa đen với một khoảng trắng gây ra vấn đề. Tôi nhận thấy điều này cũng trả về bằng nhau: if '' = SPACE (1) print 'bằng' else print 'không bằng' Tôi không thực sự quan tâm đến việc lấy độ dài thực, tôi chỉ bối rối tại sao khi tôi tìm kiếm khoảng trắng trong một cột, tất cả các cột là chuỗi trống đã được trả về.
jhale

Ngoài ra, thông tin tốt đẹp về tuyên bố LIKE. Tôi đoán đạo lý của câu chuyện là cố gắng không đưa mình vào vị trí mà bạn cần so sánh một khoảng trắng và một chuỗi trống.
jhale

2
Vấn đề lớn hơn so với việc so sánh một không gian với một chuỗi trống. So sánh bất kỳ hai chuỗi nào kết thúc bằng một số khoảng trắng khác nhau thể hiện cùng một hành vi.
JohnFx 09/09/09

3
@butterchicken: Xin lỗi vì bài đăng muộn như vậy, tôi chỉ thấy câu hỏi này, nhưng khi tôi chạy câu hỏi này (câu cuối cùng) trên tôi sql-server-2008 r2nhận được , @Space Not Like @Space2 @Space2 Not Like @Space . Bất kỳ ý tưởng tại sao?
Razort4x

1
Khẳng định trên SQL Server 2012 & SQL Server 2014, kết quả là@Space Not Like @Space2 @Space2 Not Like @Space
Chỉ cần một người học

19

Toán tử = là T-SQL không quá "bằng" vì nó là "cùng một từ / cụm từ, theo sự đối chiếu ngữ cảnh của biểu thức" và LEN là "số ký tự trong từ / cụm từ." Không có đối chiếu nào coi các khoảng trống ở cuối là một phần của từ / cụm từ đứng trước chúng (mặc dù chúng coi các khoảng trống ở đầu là một phần của chuỗi mà chúng đứng trước).

Nếu bạn cần phân biệt 'this' với 'this', bạn không nên sử dụng toán tử "là cùng một từ hoặc cụm từ" vì 'this' và 'this' là cùng một từ.

Đóng góp vào cách = hoạt động là ý tưởng rằng toán tử chuỗi-bình đẳng nên phụ thuộc vào nội dung của đối số của nó và vào ngữ cảnh đối chiếu của biểu thức, nhưng nó không nên phụ thuộc vào loại của đối số, nếu chúng đều là kiểu chuỗi .

Khái niệm ngôn ngữ tự nhiên về "đây là cùng một từ" thường không đủ chính xác để có thể được nắm bắt bởi một toán tử toán học như =, và không có khái niệm về kiểu chuỗi trong ngôn ngữ tự nhiên. Bối cảnh (tức là đối chiếu) quan trọng (và tồn tại trong ngôn ngữ tự nhiên) và là một phần của câu chuyện, và các thuộc tính bổ sung (một số có vẻ kỳ quặc) là một phần của định nghĩa = để làm cho nó được xác định rõ ràng trong thế giới phi tự nhiên của dữ liệu.

Về vấn đề loại, bạn sẽ không muốn các từ thay đổi khi chúng được lưu trữ trong các loại chuỗi khác nhau. Ví dụ: các loại VARCHAR (10), CHAR (10) và CHAR (3) đều có thể chứa các đại diện của từ 'cat', và? = 'cat' nên để chúng tôi quyết định xem một giá trị thuộc bất kỳ loại nào trong số này có chứa từ 'cat' hay không (với các vấn đề về trường hợp và trọng âm được xác định bởi đối chiếu).

Phản hồi cho bình luận của JohnFx:

Xem Sử dụng dữ liệu char và varchar trong Sách trực tuyến. Trích dẫn từ trang đó, nhấn mạnh của tôi:

Mỗi giá trị dữ liệu char và varchar có một đối chiếu. Các phép đối chiếu xác định các thuộc tính như các mẫu bit được sử dụng để đại diện cho từng ký tự, các quy tắc so sánh và độ nhạy đối với cách viết hoa hoặc nhấn giọng.

Tôi đồng ý rằng nó có thể dễ tìm hơn, nhưng nó đã được ghi lại.

Cũng cần lưu ý rằng ngữ nghĩa của SQL, trong đó = liên quan đến dữ liệu trong thế giới thực và bối cảnh so sánh (trái ngược với điều gì đó về các bit được lưu trữ trên máy tính) đã là một phần của SQL trong một thời gian dài. Tiền đề của RDBMS và SQL là sự đại diện trung thực của dữ liệu trong thế giới thực, do đó nó hỗ trợ đối chiếu nhiều năm trước khi những ý tưởng tương tự (chẳng hạn như CultureInfo) xâm nhập vào lĩnh vực của các ngôn ngữ giống như Algol. Tiền đề của những ngôn ngữ đó (ít nhất là cho đến gần đây) là giải quyết vấn đề trong kỹ thuật, không phải quản lý dữ liệu kinh doanh. (Gần đây, việc sử dụng các ngôn ngữ tương tự trong các ứng dụng phi kỹ thuật như tìm kiếm đang có một số bước tiến, nhưng Java, C #, v.v. vẫn đang vật lộn với nguồn gốc phi kinh doanh của chúng.)

Theo tôi, thật không công bằng khi chỉ trích SQL là khác với "hầu hết các ngôn ngữ lập trình." SQL được thiết kế để hỗ trợ một khuôn khổ cho mô hình dữ liệu kinh doanh rất khác với kỹ thuật, vì vậy ngôn ngữ cũng khác (và tốt hơn cho mục tiêu của nó).

Rất tiếc, khi SQL lần đầu tiên được chỉ định, một số ngôn ngữ không có bất kỳ kiểu chuỗi tích hợp nào. Và trong một số ngôn ngữ vẫn còn, toán tử bằng giữa các chuỗi hoàn toàn không so sánh dữ liệu ký tự, nhưng so sánh các tham chiếu! Tôi sẽ không ngạc nhiên nếu trong một hoặc hai thập kỷ nữa, ý tưởng == phụ thuộc vào văn hóa trở thành chuẩn mực.


BOL mô tả toán tử = do đó: "So sánh sự bằng nhau của hai biểu thức (một toán tử so sánh)." Cho dù hành vi này có đúng hay không, bạn phải thừa nhận rằng nó cực kỳ khó hiểu và không chuẩn về cách sử dụng toán tử này trong hầu hết các ngôn ngữ lập trình. MS ít nhất nên thêm một cảnh báo vào tài liệu về hành vi này.
JohnFx

@JohnFx: Xem phản hồi quá lâu cho một bình luận của tôi trong câu trả lời của tôi.
Steve Kass 10/09/09

9

Tôi đã tìm thấy bài viết trên blog này mô tả hành vi và giải thích tại sao.

Tiêu chuẩn SQL yêu cầu so sánh chuỗi, một cách hiệu quả, chèn chuỗi ngắn hơn bằng các ký tự khoảng trắng. Điều này dẫn đến kết quả đáng ngạc nhiên là N '' = N '' (chuỗi trống bằng một chuỗi gồm một hoặc nhiều ký tự khoảng trắng) và nói chung là bất kỳ chuỗi nào cũng bằng một chuỗi khác nếu chúng chỉ khác nhau bởi dấu cách ở cuối. Đây có thể là một vấn đề trong một số bối cảnh.

Thông tin thêm cũng có trong MSKB316626


Cảm ơn. Tôi ngạc nhiên rằng nó là trong tiêu chuẩn. Tôi chắc rằng ai đó thông minh hơn tôi rất nhiều đều có lý do chính đáng cho việc này.
jhale

@John: ý bạn là viết ≠ (không phải bằng) trong nhận xét của bạn?
Steve Kass

Trích dẫn ban đầu có một lỗi mà tôi đã sao chép trực tiếp. Tôi đã cập nhật phần trích dẫn để phản ánh ý của tác giả gốc.
JohnFx

5

Có một câu hỏi tương tự cách đây không lâu, nơi tôi đã xem xét một vấn đề tương tự ở đây

Thay vì LEN(' '), hãy sử dụng DATALENGTH(' ')- điều đó mang lại cho bạn giá trị chính xác.

Các giải pháp là sử dụng một LIKEmệnh đề như được giải thích trong câu trả lời của tôi ở đó, và / hoặc bao gồm điều kiện thứ hai trong WHEREmệnh đề để kiểm tra DATALENGTH.

Hãy đọc câu hỏi đó và các liên kết trong đó.


3

Để so sánh một giá trị với một khoảng trắng, bạn cũng có thể sử dụng kỹ thuật này để thay thế cho câu lệnh LIKE:

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'

0

Cách phân biệt các bản ghi trên select với các trường char / varchar trên máy chủ sql: ví dụ:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

hy vọng

mykey (int) | myfield (varchar10)

1 | 'dữ liệu '

thu được

mykey | cánh đồng của tôi

1 | 'dữ liệu' 2 | 'dữ liệu '

ngay cả khi tôi viết select mykey, myfield from mytable where myfield = 'data'(không có ô trống cuối cùng) tôi cũng nhận được kết quả tương tự.

làm thế nào tôi giải quyết? Trong chế độ này:

select mykey, myfield
from mytable
where myfield = @mayvar 
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)

và nếu có một chỉ mục trên myfield, nó sẽ được sử dụng trong từng trường hợp.

Tôi hy vọng nó sẽ hữu ích.


0

Một cách khác là đặt nó trở lại trạng thái mà không gian có giá trị. ví dụ: thay thế khoảng trắng bằng một ký tự được biết như dấu _

if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
    print 'equal'
else
    print 'not equal'

lợi nhuận: không bằng

Không lý tưởng, và có thể chậm, nhưng là một cách nhanh chóng khác để tiến lên khi cần một cách nhanh chóng.


0

Đôi khi người ta phải xử lý khoảng trắng trong dữ liệu, có hoặc không có bất kỳ ký tự nào khác, mặc dù ý tưởng sử dụng Null tốt hơn - nhưng không phải lúc nào cũng có thể sử dụng được. Tôi đã gặp phải tình huống được mô tả và giải quyết nó theo cách này:

... where ('>' + @space + '<') <> ('>' + @space2 + '<')

Tất nhiên bạn sẽ không làm điều đó đối với số lượng lớn dữ liệu nhưng nó hoạt động nhanh chóng và dễ dàng đối với hàng trăm dòng ...


1
Câu hỏi đặt ra là tại sao máy chủ SQL lại hoạt động như vậy chứ không phải cách xử lý hành vi đó nói chung. jhale có lẽ không muốn sửa đổi mã chương trình của mình, chỉ thay đổi cấu hình máy chủ của mình.
Lutz Prechelt
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.