Funkcja LEN bez spacji końcowych w SQL Server

109

Mam następującą tabelę testową w programie SQL Server 2005:

CREATE TABLE [dbo].[TestTable]
(
 [ID] [int] NOT NULL,
 [TestField] [varchar](100) NOT NULL
) 

Wypełniony:

INSERT INTO TestTable (ID, TestField) VALUES (1, 'A value');   -- Len = 7
INSERT INTO TestTable (ID, TestField) VALUES (2, 'Another value      '); -- Len = 13 + 6 spaces

Kiedy próbuję znaleźć długość TestField za pomocą funkcji SQL Server LEN (), nie liczy spacji końcowych - np:

-- Note: Also results the grid view of TestField do not show trailing spaces (SQL Server 2005).
SELECT 
 ID, 
 TestField, 
 LEN(TestField) As LenOfTestField, -- Does not include trailing spaces
FROM 
 TestTable

Jak uwzględnić końcowe spacje w wyniku długości?

Jason Snelders
źródło
1
Myślę, że prawdziwym rozwiązaniem może być naprawienie zepsutego oprogramowania przez Microsoft. Głosuj tutaj: feedback.azure.com/forums/908035-sql-server/suggestions/…
QA Collective

Odpowiedzi:

125

Jest to wyraźnie udokumentowane przez firmę Microsoft w witrynie MSDN pod adresem http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx , który stwierdza, że ​​LEN „zwraca liczbę znaków określonego wyrażenia ciągu, z wyłączeniem końcowe spacje ”. Jest to jednak łatwy szczegół do przeoczenia, jeśli nie jesteś ostrożny.

Zamiast tego należy użyć funkcji DATALENGTH - patrz http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx - która „zwraca liczbę bajtów używanych do reprezentowania dowolnego wyrażenia”.

Przykład:

SELECT 
    ID, 
    TestField, 
    LEN(TestField) As LenOfTestField,           -- Does not include trailing spaces
    DATALENGTH(TestField) As DataLengthOfTestField      -- Shows the true length of data, including trailing spaces.
FROM 
    TestTable
Jason Snelders
źródło
52
UWAGA: DATALENGTHWynik należy również podzielić przez 2, jeśli testowane wyrażenie ma typ szerokiego znaku (Unicode; nchar, nvarchar lub ntext), ponieważ wynik jest w bajtach , a nie w znakach .
devstuff,
7
Również w przypadku varcharitp. Może to zależeć od sortowania i nawet proste dzielenie przez 2 nie jest wiarygodne. Zobacz przykład tutaj
Martin Smith
18
Użyłbym LEN(REPLACE(expr, ' ', '_')). Powinno to działać z ciągami znaków varchari nvarchari zawierającymi specjalne znaki sterujące Unicode.
Olivier Jacot-Descombes
6
-1, DATALENGTH()nie powinno być traktowane jako alternatywny sposób liczenia znaków, ponieważ liczy bajty zamiast znaków i ma to znaczenie, gdy reprezentuje ten sam ciąg w VARCHAR/ NVARCHAR.
binki
5
Począwszy od SQL Server 2012, kolumny Unicode z sortowaniem w wersji 100 obsługują teraz pary zastępcze. Oznacza to, że pojedynczy znak może zająć do 4 bajtów, powodując niepowodzenie dzielenia przez dwa. Zobacz msdn .
Frédéric
85

Możesz użyć tej sztuczki:

LEN (Str + 'x') - 1

Serge
źródło
15
Czy możesz nas oświecić lepszymi alternatywami? Na pewno nie jest to długość danych.
Serge
15
Zdecydowanie nie zgadzam się, że użycie niespójnej metody (w niektórych przypadkach dzieli się wynik przez 2, a czasem nie) jest lepszą opcją. Może jednak moja metoda ma prawie zerową wydajność.
Serge
5
Metoda @usr Serge'a jest najlepsza, IMHO. Prosty i elegancki. DATALENGTH jest skomplikowane: zależne od typu pojedynczego / dwubajtowego, sortowania / zależne od języka itp.
Pan TA
10
To jak dotąd najlepsze, eleganckie rozwiązanie. Nie obchodzi mnie, czy czuje się jak włamanie, czy nie (w kodowaniu nie chodzi o uczucia), naprawdę zależy mi na tym, że to rozwiązanie nie ma skutków ubocznych. Mogę zmienić typ danych varchar / nvarchar i nadal działa. Dobra robota.
Mike Keskinov
5
Istnieje zastrzeżenie z powodu tego efektu ubocznego. Jeśli pracujesz ze zmienną typu nvarchar (4000), a twoja zmienna zawiera ciąg 4000 znaków, dodany znak zostanie zignorowany i otrzymasz zły wynik (len SQL ignoruje spacje końcowe, minus 1 odejmujesz).
toporek - zrobiono z SOverflow
17

Używam tej metody:

LEN(REPLACE(TestField, ' ', '.'))

Wolę to od DATALENGTH, ponieważ działa to z różnymi typami danych i wolę to niż dodawanie znaku na końcu, ponieważ nie musisz się martwić o przypadek krawędzi, w którym twój ciąg ma już maksymalną długość.

Uwaga: przed użyciem przetestowałbym wydajność w odniesieniu do bardzo dużego zestawu danych; chociaż właśnie przetestowałem go na 2M rzędach i nie był wolniejszy niż LEN bez REPLACE ...

TTT
źródło
14

„Jak uwzględnić końcowe spacje w wyniku długości?”

Możesz poprosić kogoś o zgłoszenie prośby o ulepszenie SQL Server / zgłoszenie błędu, ponieważ prawie wszystkie wymienione tutaj obejścia tego niezwykle prostego problemu mają pewne wady lub są nieefektywne. Nadal wydaje się, że tak jest w SQL Server 2012. Funkcja automatycznego przycinania może pochodzić z ANSI / ISO SQL-92, ale wydaje się, że są jakieś dziury (lub brak ich zliczania).

Zagłosuj na „Dodaj ustawienie, aby LEN liczyło końcowe spacje” tutaj:

https://feedback.azure.com/forums/908035-sql-server/suggestions/34673914-add-setting-so-len-counts-trailing-whitespace

Wycofane łącze Connect: https://connect.microsoft.com/SQLServer/feedback/details/801381

crokusek
źródło
2
datalengthRozwiązanie jest nawet gorzej, począwszy od serwera SQL 2012, ponieważ nie obsługuje zastępczych par w UTF-16, co oznacza, postać może używać maksymalnie 4 bajty. Naprawdę nadszedł czas, aby naprawić lenfunkcję zgodną z ANSI lub przynajmniej zapewnić dedykowaną funkcję do liczenia znaków, w tym spacji końcowych.
Frédéric
1
W tym celu należy częściej używać łącza opinii. Zaskakujące jest, że ten problem można przeszukiwać tylko w Internecie. Spędziłem prawie 2 godziny, próbując dowiedzieć się, gdzie popełniłem błąd we własnym kodzie, zanim nawet pomyślałem, że funkcja LEN () była przyczyną mojego rozłączenia.
Takophiliac
Zgadzam się z tym, ale powinienem umożliwić parametrowi wycinanie białych znaków .. ponieważ znacznie ułatwia to porównywanie ciągów z EF, bez konieczności sprawdzania, czy podczas budowania wyrażenia iqueryable są uwzględnione białe znaki.
ganjeii
9

Wystąpiły problemy z dwoma najczęściej głosowanymi odpowiedziami. Polecająca odpowiedź DATALENGTHjest podatna na błędy programisty. Wynik DATALENGTHnależy podzielić przez 2 dla NVARCHARtypów, ale nie dla VARCHARtypów. Wymaga to znajomości typu, którego długość otrzymujesz, a jeśli ten typ się zmieni, musisz pilnie zmieniać miejsca, z których korzystałeś DATALENGTH.

Jest też problem z najbardziej pozytywną odpowiedzią (przyznaję, że był to mój ulubiony sposób, dopóki ten problem mnie nie ugryzł). Jeśli rzecz, której otrzymujesz długość, jest typu NVARCHAR(4000)i faktycznie zawiera ciąg 4000 znaków, SQL zignoruje dołączony znak, zamiast niejawnie rzutować wynik na NVARCHAR(MAX). Wynik końcowy to nieprawidłowa długość. To samo stanie się z VARCHAR (8000).

To, co znalazłem, działa, jest prawie tak szybkie, jak zwykłe stare LEN, jest szybsze niż w LEN(@s + 'x') - 1przypadku dużych ciągów i nie zakłada, że ​​podstawowa szerokość znaku jest następująca:

DATALENGTH(@s) / DATALENGTH(LEFT(LEFT(@s, 1) + 'x', 1))

Pobiera długość danych, a następnie dzieli przez długość danych pojedynczego znaku z ciągu. Dodanie „x” obejmuje przypadek, w którym ciąg jest pusty (co w tym przypadku dałoby podzielenie przez zero). Działa @sto niezależnie od tego, czy jest, VARCHARczy NVARCHAR. Wykonanie LEFT1 znaku przed dołączeniem pozwala zaoszczędzić trochę czasu, gdy ciąg jest duży. Problem z tym polega jednak na tym, że nie działa poprawnie z łańcuchami zawierającymi pary zastępcze.

W komentarzu do zaakceptowanej odpowiedzi wspomniano o innym sposobie użycia REPLACE(@s,' ','x'). Ta technika daje poprawną odpowiedź, ale jest o kilka rzędów wielkości wolniejsza niż inne techniki, gdy struna jest duża.

Biorąc pod uwagę problemy wprowadzone przez pary zastępcze w dowolnej używanej technice DATALENGTH, myślę, że najbezpieczniejsza metoda, która daje prawidłowe odpowiedzi, to:

LEN(CONVERT(NVARCHAR(MAX), @s) + 'x') - 1

Jest to szybsze niż REPLACEtechnika i znacznie szybsze w przypadku dłuższych strun. Zasadniczo ta technika jest LEN(@s + 'x') - 1techniką, ale z ochroną dla przypadku krawędzi, w którym łańcuch ma długość 4000 (dla nvarchar) lub 8000 (dla varchar), więc nawet w tym przypadku podano poprawną odpowiedź. Powinien również poprawnie obsługiwać łańcuchy z parami zastępczymi.

toporek - zrobiono z SOverflow
źródło
1
Niestety, ta odpowiedź nie działa już dla ciągów zawierających pary zastępcze w SQL Server 2012. Uruchomienie operacji na N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SCdaje 4, podczas gdy LENdaje 3.
Douglas
9
@Douglas - to przydatne informacje. Gdyby tylko Microsoft dał nam wersję LEN, która nie ignoruje końcowych spacji.
toporek - zrobiony z SOverflow
5

Musisz również upewnić się, że Twoje dane są faktycznie zapisane z końcowymi spacjami. Kiedy ANSI PADDING jest WYŁĄCZONE (inne niż domyślne):

Końcowe spacje w wartościach znakowych wstawionych do kolumny varchar są przycinane.

Remus Rusanu
źródło
3
Myślę, że nie należy wyłączać ANSI PADDING, ponieważ to ustawienie jest przestarzałe. Posiadanie go na niestandardowej wartości powoduje wiele drobnych problemów.
usr
4

LEN domyślnie wycina końcowe spacje, więc stwierdziłem, że działa, gdy przesuwasz je do przodu

(LEN (REVERSE (TestField))

Więc jeśli chcesz, możesz powiedzieć

SELECT
t.TestField,
LEN(REVERSE(t.TestField)) AS [Reverse],
LEN(t.TestField) AS [Count]
FROM TestTable t
WHERE LEN(REVERSE(t.TestField)) <> LEN(t.TestField)

Nie używaj tego oczywiście do wiodących spacji.

Joey
źródło
9
Teraz przycina wiodące spacje zamiast spacji końcowych. Tego samego dnia, inny problem :)
Reversed Engineer
@DaveBoltman Moja sugestia jest prawdopodobnie jeszcze bardziej zawiła, ale możesz dodatkowo porównać z długością TRIM.
Brian J,
To odwraca błąd, w którym spacje wiodące nie są liczone zamiast spacji końcowych. Zobacz następujący kod: declare @TestField varchar(10); SET @TestField = ' abc '; -- Length with spaces is 5. select LEN(REVERSE(@TestField)) -- Returns 4 select LEN(@TestField) -- Returns 4
Metalogic
1

Powinieneś zdefiniować funkcję CLR, która zwraca pole Długość ciągu, jeśli nie lubisz łączenia ciągów. Używam LEN('x' + @string + 'x') - 2w moich przypadkach użycia w produkcji.

obratim
źródło
0

Jeśli nie podoba ci się z DATALENGTHpowodu obaw n / varchar, co powiesz na:

select DATALENGTH(@var)/isnull(nullif(DATALENGTH(left(@var,1)),0),1)

co jest sprawiedliwe

select DATALENGTH(@var)/DATALENGTH(left(@var,1))

owinięte ochroną dzielenia przez zero.

Dzieląc przez DATALENGTH pojedynczego znaku, otrzymujemy znormalizowaną długość.

(Oczywiście nadal występują problemy z parami zastępczymi, jeśli jest to problem).

dsz
źródło
-4

użyj SELECT DATALENGTH („ciąg”)

aman6496
źródło
2
właśnie powtórzyłeś odpowiedzi innych osób sprzed 7 lat i nie przedstawiłeś nic nowego ani nawet nie wyjaśniłeś, co dajesz, ani jak odpowiada na to pytanie.
Jpsh