LEN-Funktion ohne nachgestellte Leerzeichen in SQL Server

109

Ich habe die folgende Testtabelle in SQL Server 2005:

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

Bestückt mit:

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

Wenn ich versuche, die Länge von TestField mit der SQL Server LEN () -Funktion zu ermitteln, werden die nachfolgenden Leerzeichen nicht gezählt - z.

-- 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

Wie füge ich die nachgestellten Leerzeichen in das Längenergebnis ein?

Jason Snelders
quelle
1
Ich denke, die wirkliche Lösung hier könnte darin bestehen, dass Microsoft die kaputte Software repariert. Stimmen Sie hier ab: feedback.azure.com/forums/908035-sql-server/suggestions/…
QA Collective

Antworten:

125

Dies wird von Microsoft in MSDN unter http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx klar dokumentiert , wobei LEN "die Anzahl der Zeichen des angegebenen Zeichenfolgenausdrucks ausschließt, ausgenommen nachgestellte Leerzeichen ". Es ist jedoch ein leicht zu übersehendes Detail, wenn Sie nicht vorsichtig sind.

Sie müssen stattdessen die DATALENGTH-Funktion verwenden - siehe http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx - die "die Anzahl der Bytes zurückgibt, die zur Darstellung eines Ausdrucks verwendet werden".

Beispiel:

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
quelle
52
HINWEIS: DATALENGTHSie müssen das Ergebnis auch durch 2 teilen, wenn der getestete Ausdruck ein breiter Zeichentyp ist (Unicode; nchar, nvarchar oder ntext), da das Ergebnis in Bytes und nicht in Zeichen angegeben ist .
Devstuff
7
Auch für varcharetc. kann dies kollationsabhängig sein und nicht einmal eine direkte Division durch 2 ist zuverlässig. Siehe Beispiel hier
Martin Smith
18
Ich würde verwenden LEN(REPLACE(expr, ' ', '_')). Dies sollte mit varcharund nvarcharund Zeichenfolgen funktionieren, die spezielle Unicode-Steuerzeichen enthalten.
Olivier Jacot-Descombes
6
-1 DATALENGTH()sollte nicht als alternative Methode zum Zählen von Zeichen angesehen werden, da Bytes anstelle von Zeichen gezählt werden. Dies ist wichtig, wenn dieselbe Zeichenfolge in VARCHAR/ dargestellt wird NVARCHAR.
Binki
5
Ab SQL Server 2012 unterstützen Unicode-Spalten mit Kollatierungen der Version 100 jetzt Ersatzpaare. Dies bedeutet, dass ein einzelnes Zeichen bis zu 4 Bytes verwenden kann, wodurch die Division durch zwei Tricks fehlschlägt. Siehe msdn .
Frédéric
85

Sie können diesen Trick verwenden:

LEN (Str + 'x') - 1

Serge
quelle
15
Könnten Sie uns bitte die besseren Alternativen erläutern? Datalength ist sicher nicht.
Serge
15
Ich bin nicht der Meinung, dass die Verwendung einer inkonsistenten Methode (in einigen Fällen teilen Sie das Ergebnis durch 2 und manchmal nicht) eine bessere Option ist. Vielleicht gibt es bei meiner Methode jedoch einen Leistungseinbruch nahe Null.
Serge
5
Die Methode von @usr Serge ist meiner Meinung nach die beste. Einfach und elegant. DATALENGTH ist kompliziert: Einzel- / Doppelbyte-Typ abhängig, Kollatierung / Sprache abhängig usw.
Herr TA
10
Dies ist die bisher beste und eleganteste Lösung. Es ist mir egal, ob es sich wie ein Hack anfühlt oder nicht (beim Codieren geht es nicht um Gefühle), es ist mir wirklich wichtig, dass diese Lösung keine Nebenwirkungen hat. Ich kann den Datentyp varchar / nvarchar ändern und es funktioniert immer noch. Gut gemacht.
Mike Keskinov
5
Aufgrund dieser Nebenwirkung gibt es eine Einschränkung. Wenn Sie mit einer Variablen vom Typ nvarchar (4000) arbeiten und Ihre Variable eine 4000-Zeichenfolge enthält, wird das hinzugefügte Zeichen ignoriert und Sie erhalten das falsche Ergebnis (SQL-Len, das nachgestellte Leerzeichen ignoriert, abzüglich der 1 Sie subtrahieren).
Beil - fertig mit SOverflow
17

Ich benutze diese Methode:

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

Ich bevorzuge dies gegenüber DATALENGTH, da dies mit verschiedenen Datentypen funktioniert, und ich bevorzuge es gegenüber dem Hinzufügen eines Zeichens am Ende, da Sie sich nicht um den Randfall kümmern müssen, in dem Ihre Zeichenfolge bereits die maximale Länge hat.

Hinweis: Ich würde die Leistung testen, bevor ich sie mit einem sehr großen Datensatz verwende. obwohl ich es gerade gegen 2M Zeilen getestet habe und es ohne REPLACE nicht langsamer als LEN war ...

TTT
quelle
14

"Wie füge ich die nachgestellten Leerzeichen in das Längenergebnis ein?"

Sie veranlassen jemanden, eine SQL Server-Erweiterungsanforderung / einen Fehlerbericht einzureichen, da fast alle hier aufgeführten Problemumgehungen für dieses erstaunlich einfache Problem Mängel aufweisen oder ineffizient sind. Dies scheint in SQL Server 2012 immer noch der Fall zu sein. Die Funktion zum automatischen Trimmen stammt möglicherweise aus ANSI / ISO SQL-92, aber es scheint einige Lücken zu geben (oder sie nicht zu zählen).

Bitte stimmen Sie hier ab "Einstellung hinzufügen, damit LEN nachgestellte Leerzeichen zählt":

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

Link "Connect im Ruhestand": https://connect.microsoft.com/SQLServer/feedback/details/801381

crokusek
quelle
2
Die datalengthLösung ist ab SQL Server 2012 noch schlechter, da sie jetzt Ersatzpaare in UTF-16 unterstützt, was bedeutet, dass ein Zeichen bis zu 4 Byte verwenden kann. Es ist wirklich an der Zeit, die lenFunktion zur Einhaltung von ANSI festzulegen oder zumindest eine spezielle Funktion zum Zählen von Zeichen einschließlich nachfolgender Leerzeichen bereitzustellen.
Frédéric
1
Der Feedback-Link muss dafür stärker genutzt werden. Es ist verwirrend, dass dieses Problem nur über das Internet gesucht werden kann. Ich habe fast 2 Stunden damit verbracht herauszufinden, wo ich einen Fehler in meinem eigenen Code gemacht habe, bevor ich überhaupt daran gedacht habe, dass die LEN () - Funktion die Ursache für meine Trennung war.
Takophiliac
Ich bin damit einverstanden, sollte aber zulassen, dass ein Parameter Leerzeichen herausschneidet. Dies erleichtert den Vergleich von Zeichenfolgen mit EF erheblich, da beim Erstellen des iqueryable-Ausdrucks nicht überprüft werden muss, ob Leerzeichen enthalten sind.
Ganjeii
9

Es gibt Probleme mit den beiden am besten bewerteten Antworten. Die empfohlene Antwort DATALENGTHist anfällig für Programmiererfehler. Das Ergebnis von DATALENGTHmuss für NVARCHARTypen durch 2 geteilt werden, nicht jedoch für VARCHARTypen. Dies erfordert Kenntnisse über den Typ, dessen Länge Sie erhalten. Wenn sich dieser Typ ändert, müssen Sie die von Ihnen verwendeten Stellen sorgfältig ändern DATALENGTH.

Es gibt auch ein Problem mit der am besten bewerteten Antwort (was ich zugeben muss, war meine bevorzugte Methode, bis dieses Problem mich gebissen hat). Wenn das Objekt, dessen Länge Sie erhalten, vom Typ NVARCHAR(4000)ist und tatsächlich eine Zeichenfolge mit 4000 Zeichen enthält, ignoriert SQL das angehängte Zeichen, anstatt das Ergebnis implizit umzuwandeln NVARCHAR(MAX). Das Endergebnis ist eine falsche Länge. Das gleiche passiert mit VARCHAR (8000).

Was ich gefunden habe, funktioniert, ist fast so schnell wie normal LEN, ist schneller als LEN(@s + 'x') - 1bei großen Zeichenfolgen und geht nicht davon aus, dass die zugrunde liegende Zeichenbreite wie folgt ist:

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

Dies erhält die Datenlänge und dividiert dann durch die Datenlänge eines einzelnen Zeichens aus der Zeichenfolge. Der Anhang von 'x' deckt den Fall ab, in dem die Zeichenfolge leer ist (was in diesem Fall eine Division durch Null ergeben würde). Dies funktioniert , ob @sist VARCHARoder NVARCHAR. Doing die LEFTvon 1 Zeichen vor dem Anfügen Rasur einige Zeit , wenn die Zeichenfolge groß ist . Das Problem dabei ist jedoch, dass es mit Zeichenfolgen, die Ersatzpaare enthalten, nicht richtig funktioniert.

Es gibt eine andere Möglichkeit, die in einem Kommentar zur akzeptierten Antwort mit erwähnt wird REPLACE(@s,' ','x'). Diese Technik gibt die richtige Antwort, ist jedoch einige Größenordnungen langsamer als die anderen Techniken, wenn die Zeichenfolge groß ist.

Angesichts der Probleme, die Ersatzpaare bei jeder verwendeten Technik verursachen, DATALENGTHdenke ich, dass die sicherste Methode, die mir die richtigen Antworten gibt, die folgenden ist:

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

Dies ist schneller als die REPLACETechnik und viel schneller bei längeren Saiten. Grundsätzlich ist diese Technik die LEN(@s + 'x') - 1Technik, aber mit Schutz für den Randfall, in dem die Zeichenfolge eine Länge von 4000 (für nvarchar) oder 8000 (für varchar) hat, so dass auch dafür die richtige Antwort gegeben wird. Es sollte auch Zeichenfolgen mit Ersatzpaaren korrekt behandeln.

Beil - fertig mit SOverflow
quelle
1
Leider funktioniert diese Antwort nicht mehr für Zeichenfolgen, die Ersatzpaare in SQL Server 2012 enthalten. Wenn Sie Ihre Operation auf ausführen, erhalten Sie N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SC4, während Sie LEN3 erhalten.
Douglas
9
@ Douglas - Das sind nützliche Informationen. Wenn Microsoft uns nur eine Version von LEN geben würde, die nachgestellte Leerzeichen nicht ignoriert.
Beil - fertig mit SOverflow
5

Sie müssen auch sicherstellen, dass Ihre Daten tatsächlich mit den nachfolgenden Leerzeichen gespeichert werden. Wenn ANSI PADDING ausgeschaltet ist (nicht standardmäßig):

Nachfolgende Leerzeichen in Zeichenwerten, die in eine Varchar-Spalte eingefügt werden, werden abgeschnitten.

Remus Rusanu
quelle
3
Ich denke, Sie sollten ANSI PADDING nicht deaktivieren, da diese Einstellung veraltet ist. Ein nicht standardmäßiger Wert verursacht viele kleine Probleme.
usr
4

LEN schneidet standardmäßig nachgestellte Leerzeichen ab, daher habe ich festgestellt, dass dies funktioniert, wenn Sie sie nach vorne verschieben

(LEN (REVERSE (TestField))

Wenn Sie also wollten, könnten Sie sagen

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)

Verwenden Sie dies natürlich nicht für führende Räume.

Joey
quelle
9
Jetzt werden führende Leerzeichen anstelle von nachfolgenden Leerzeichen abgeschnitten. Gleicher Tag, anderes Problem :)
Reversed Engineer
@ DaveBoltman Mein Vorschlag ist wahrscheinlich noch komplizierter, aber Sie können ihn zusätzlich mit der TRIM'ed-Länge vergleichen.
Brian J
Dies kehrt den Fehler um, bei dem führende Leerzeichen nicht nach nachfolgenden Leerzeichen gezählt werden. Siehe den folgenden Code: 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

Sie sollten eine CLR-Funktion definieren, die das Feld Länge des Strings zurückgibt, wenn Sie die Konkatination von Strings nicht mögen. Ich verwende LEN('x' + @string + 'x') - 2in meiner Produktion Anwendungsfälle.

obratim
quelle
0

Wenn Sie das DATALENGTHwegen n / varchar Bedenken nicht mögen, wie wäre es mit:

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

das ist nur

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

umwickelt mit einem durch Null teilenden Schutz.

Durch Teilen durch die DATALENGTH eines einzelnen Zeichens erhalten wir die normalisierte Länge.

(Natürlich gibt es immer noch Probleme mit Ersatzpaaren, wenn das ein Problem ist.)

dsz
quelle
-4

benutze SELECT DATALENGTH ('string')

aman6496
quelle
2
Sie haben gerade die Antworten anderer von vor 7 Jahren angepasst und nichts Neues bereitgestellt oder sogar erklärt, was Ihre Antwort bewirkt oder wie diese Frage beantwortet wird.
Jpsh