SQL Server 2008 Boş Dize ve Boşluk


83

Bu sabah biraz tuhaf bir şeyle karşılaştım ve yorum için sunayım dedim.

Birisi aşağıdaki SQL sorgusunun SQL 2008 ile çalıştırıldığında neden 'eşit' yazdırdığını açıklayabilir. Db uyumluluk seviyesi 100 olarak ayarlanmıştır.

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

Ve bu 0 döndürür:

select (LEN(' '))

Alanı otomatik olarak kırpıyor gibi görünüyor. SQL Server'ın önceki sürümlerinde durumun bu olup olmadığı konusunda hiçbir fikrim yok ve artık onu test edecek herhangi bir çevrem yok.

Bununla karşılaştım çünkü bir üretim sorgusu yanlış sonuçlar veriyordu. Bu davranışı hiçbir yerde belgelenmiş bulamıyorum.

Bu konuda bilgisi olan var mı?


2
SQL 2005: len ('') seç 0
Mayo

1
Aynı şeyi Sql Server 2000'de de yapıyor.
Pierre-Alain Vigeant

1
Bu büyüleyici bir soru. Eşleşip eşleşmediklerine bakılmaksızın herhangi bir dizeye kaç boşluk koyarsanız koyun eşit olarak dönüyor gibi görünüyor. Daha fazla deney yaptıktan sonra, karşılaştırmadan önce eşitlik operatörünün her iki tarafında da etkin bir şekilde RTRIM yaptığını fark ettim. Görünüşe göre UZUNLUK işlevi hakkında bir yanıt almışsınız, ancak sorunun eşitlik kısmı için "TSQ'da değişkenler ve eşitlik çetrefilli" den daha kapsamlı bir yanıtla gerçekten ilgileniyorum.
JohnFx

Oracle'ın da bunu yaptığına inanıyorum.
quillbreaker

Genel olarak boş dizge saklamanın kötü bir fikir olduğunu düşünüyorum ve bu sebeplerden biri. Null kullanımını tercih ediyorum ve insanlar boş dize gibi bir değere veya normal aralığın dışında bir veri yolu gibi bir değere dönüştürmeye çalıştıklarında birçok sorun buluyorum.
HLGEM

Yanıtlar:


90

varchars ve eşitlik TSQL'de çetrefilli. LENFonksiyon diyor ki:

Verilen dize ifadesinin bayt sayısı yerine karakter sayısını döndürür, sondaki boşlukları hariç tutar .

Söz konusu verilerin DATALENGTHgerçek bytesayısını elde etmek için kullanmanız gerekir . Unicode verileriniz varsa, bu durumda alacağınız değerin metnin uzunluğu ile aynı olmayacağını unutmayın.

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

İfadelerin eşitliği söz konusu olduğunda, iki dizi eşitlik açısından şu şekilde karşılaştırılır:

  • Daha kısa dize alın
  • Uzunluk, daha uzun ipin uzunluğuna eşit olana kadar boşluklarla doldurun
  • İkisini karşılaştır

Beklenmedik sonuçlara neden olan orta adımdır - bu adımdan sonra, beyaz boşluğu beyaz boşlukla etkili bir şekilde karşılaştırırsınız - dolayısıyla eşit olarak görülürler.

LIKE="boşluklar" durumundan daha iyi davranır çünkü eşleştirmeye çalıştığınız kalıp üzerinde boşluk doldurma yapmaz:

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

eqSüre verecek :

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

Verecek ne

LIKEYine de dikkatli olun : simetrik değildir: takip eden beyaz boşluğu kalıpta (RHS) önemli olarak değerlendirir ancak eşleşme ifadesinde (LHS) değil. Aşağıdakiler buradan alınmıştır :

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
Güzel cevap. Bunu LEN belgelerinde fark etmemiştim. Yine de LEN ile sınırlı değil. SAĞ ve SOL işlevi benzer davranış sergiler, ancak burada belgelenmemiştir. Soruna neden olan bir boşlukla gerçek gibi görünüyor. Bunun da eşit olduğunu fark ettim: eğer '' = SPACE (1) print 'eşittir' else print 'eşit değil' Gerçek uzunluğu elde etmekle gerçekten ilgilenmiyorum, neden bir boşluk ararken kafam karışmıştı. bir sütun, boş dizeler olan tüm sütunlar döndürüldü.
jhale

Ayrıca, LIKE ifadesi hakkında güzel bilgiler. Sanırım hikayenin ahlakı, bir boşlukla boş bir dizgiyi karşılaştırmanız gereken konuma kendinizi kaptırmamaya çalışmak.
jhale

2
Sorun, bir boşluğu boş bir dizeyle karşılaştırmaktan daha büyük. Farklı sayıda boşlukla biten herhangi iki dizeyi karşılaştırmak aynı davranışı sergiler.
JohnFx

3
@butterchicken: Bu kadar geç bir gönderi için özür dilerim, bu soruyu yeni gördüm, ama bunu (sonuncusu) çalıştırdığımda sql-server-2008 r2anladım @Space Not Like @Space2 @Space2 Not Like @Space . Herhangi bir fikrin neden?
Razort4x

1
SQL Server 2012 ve SQL Server 2014'te onaylandı, sonuç@Space Not Like @Space2 @Space2 Not Like @Space
Sadece bir öğrenci

19

= Operatörü T-SQL olduğu gibi "eşittir" değildir "ifadenin bağlamının harmanlamasına göre aynı kelime / kelime öbeğidir" ve LEN "kelime / kelime öbeğindeki karakter sayısıdır." Hiçbir harmanlama, arkadaki boşlukları kendilerinden önceki kelime / ifadenin bir parçası olarak ele almaz (baştaki boşlukları önündeki dizenin bir parçası olarak değerlendirirler)

"Bu" ile "bu" arasındaki farkı ayırt etmeniz gerekiyorsa, "aynı kelime veya kelime öbeği" operatörünü kullanmamalısınız çünkü "bu" ve "bu" aynı kelimedir.

Yol = çalışmalarına katkıda bulunan, dize eşitliği operatörünün argümanlarının içeriğine ve ifadenin harmanlama bağlamına bağlı olması gerektiği, ancak her ikisi de dize türündeyse argümanların türlerine bağlı olmaması gerektiği fikridir. .

"Bunlar aynı kelimedir" şeklindeki doğal dil kavramı tipik olarak = gibi bir matematiksel operatör tarafından yakalanabilecek kadar kesin değildir ve doğal dilde dizgi türü kavramı yoktur. Bağlam (yani, harmanlama) önemlidir (ve doğal dilde mevcuttur) ve hikayenin bir parçasıdır ve ek özellikler (bazıları ilginç görünüyor), onu doğal olmayan dünyasında iyi tanımlanmış kılmak için = tanımının bir parçasıdır. veri.

Tür konusunda, farklı dize türlerinde depolandıklarında kelimelerin değişmesini istemezsiniz. Örneğin, VARCHAR (10), CHAR (10) ve CHAR (3) türlerinin tümü 'cat' kelimesinin temsillerini içerebilir ve? = 'kedi', bu türlerden herhangi birinin bir değerinin 'kedi' kelimesini barındırıp barındırmadığına karar vermemize izin vermelidir (büyük / küçük harf ve aksan sorunları harmanlama ile belirlenir)

JohnFx'in yorumuna yanıt:

Çevrimiçi Kitaplarda char ve varchar Verilerini Kullanma konusuna bakın . O sayfadan alıntı yapmak, benim vurgum

Her char ve varchar veri değerinin bir harmanlaması vardır. Harmanlamalar, her bir karakteri temsil etmek için kullanılan bit desenleri, karşılaştırma kuralları ve büyük / küçük harfe veya vurguya duyarlılık gibi nitelikleri tanımlar .

Bulmanın daha kolay olabileceğine katılıyorum, ancak belgelendi.

Ayrıca kayda değer, SQL'in anlambiliminin, burada = gerçek dünya verileriyle ve karşılaştırmanın bağlamı (bilgisayarda depolanan bitlerle ilgili bir şeyin aksine) uzun süredir SQL'in bir parçası olduğudur. RDBMS'lerin ve SQL'in öncülü, gerçek dünya verilerinin sadık temsilidir, dolayısıyla benzer fikirler (CultureInfo gibi) Algol benzeri dillerin alanına girmeden yıllar önce harmanlamaları desteklemektedir. Bu dillerin temeli (en azından çok yakın zamana kadar) iş verilerinin yönetimi değil, mühendislikte problem çözmekti. (Son zamanlarda, arama gibi mühendislik dışı uygulamalarda benzer dillerin kullanılması bazı ilerlemeler yaratıyor, ancak Java, C # vb. Hala ticari olmayan kökleriyle mücadele ediyor.)

Bana göre SQL'i "çoğu programlama dilinden" farklı olduğu için eleştirmek adil değil. SQL, mühendislikten çok farklı bir iş veri modellemesi çerçevesini desteklemek için tasarlanmıştır, bu nedenle dil farklıdır (ve amacı için daha iyidir).

Heck, SQL ilk belirlendiğinde, bazı dillerde herhangi bir yerleşik dize türü yoktu. Ve yine de bazı dillerde dizeler arasındaki eşittir operatörü karakter verilerini hiç karşılaştırmaz, ancak referansları karşılaştırır! Bir veya iki on yıl içinde == kültüre bağlı fikrinin norm haline gelmesi beni şaşırtmaz.


BOL, = işlecini şu şekilde açıklar: "İki ifadenin eşitliğini karşılaştırır (bir karşılaştırma operatörü)." Davranış doğru olsun veya olmasın, çoğu programlama dilinde bu operatörün kullanımı açısından son derece kafa karıştırıcı ve standart dışı olduğunu kabul etmelisiniz. MS, bu davranışla ilgili belgelere en azından bir uyarı eklemelidir.
JohnFx

@JohnFx: Cevabımda yorum yapmak için çok uzun cevabımı görün.
Steve Kass

9

Davranışı açıklayan ve nedenini açıklayan bu blog makalesini buldum .

SQL standardı, dize karşılaştırmalarının etkili bir şekilde daha kısa dizeyi boşluk karakterleriyle doldurmasını gerektirir. Bu, N '' = N '' (boş dize bir veya daha fazla boşluk karakterinden oluşan bir dizgeye eşittir) ve daha genel olarak herhangi bir dizenin, yalnızca sondaki boşluklarla farklılık gösteriyorsa başka bir dizeye eşit olması şaşırtıcı sonucuna yol açar. Bu, bazı bağlamlarda sorun olabilir.

Daha fazla bilgi MSKB316626'da da mevcuttur


Teşekkürler. Standartta olmasına şaşırdım. Eminim benden çok daha zeki birinin bunun için iyi bir nedeni vardır.
jhale

@John: Yorumunuza ≠ (eşit değil) yazmak mı istediniz?
Steve Kass

Orijinal alıntıda, doğrudan kopyaladığım bir hata vardı. Orijinal yazarın ne demek istediğini yansıtmak için alıntıyı güncelledim.
JohnFx

5

Bir süre önce, benzer bir soruna burada baktığımda benzer bir soru vardı

Bunun yerine, size doğru değeri veren - LEN(' ')kullanın DATALENGTH(' ').

Çözümler, LIKEoradaki cevabımda açıklandığı gibi bir cümle kullanmak ve / veya WHEREcümleye kontrol etmek için 2. bir koşul eklemek idi DATALENGTH.

Bu soruyu ve oradaki bağlantıları okuyun.


3

Bir değeri değişmez bir alanla karşılaştırmak için, bu tekniği LIKE ifadesine alternatif olarak da kullanabilirsiniz:

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

0

Sql sunucusunda char / varchar alanlarıyla seçili kayıtlar nasıl ayırt edilir: örnek:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

beklenen

mykey (int) | myfield (varchar10)

1 | "veri"

Elde edilen

mykey | benim alanım

1 | 'veri' 2 | "veri"

yazsam bile select mykey, myfield from mytable where myfield = 'data'(son boşluk olmadan) aynı sonuçları alıyorum.

nasıl çözdüm? Bu modda:

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

ve alanımda bir dizin varsa, her durumda kullanılacaktır.

Umarım yardımcı olur.


0

Başka bir yol da, onu mekanın değerli olduğu bir duruma geri getirmektir. örneğin: boşluğu _ gibi bilinen bir karakterle değiştirin

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

döndürür: eşit değil

İdeal değil ve muhtemelen yavaş, ancak hızlı bir şekilde ihtiyaç duyulduğunda ileriye doğru başka bir hızlı yoldur.


0

Null kullanma fikri daha iyi olsa da, her zaman kullanılabilir olmasa da, bazen verilerdeki boşluklarla, başka karakterlerle veya başka karakterlerle uğraşmak zorunda kalırsınız. Açıklanan durumla karşılaştım ve şu şekilde çözdüm:

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

Tabii ki bunu büyük miktarda veri için yapmazsınız, ancak birkaç yüz satır için hızlı ve kolay çalışır ...


1
Soru, genel olarak bu tür davranışların nasıl ele alınacağı değil, SQL sunucusunun neden olduğu gibi davrandığı idi . jhale muhtemelen program kodunu değiştirmeyi tercih etmez, yalnızca sunucu yapılandırmasını değiştirir.
Lutz Prechelt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.