Fonction LEN n'incluant pas les espaces de fin dans SQL Server

109

J'ai la table de test suivante dans SQL Server 2005:

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

Rempli de:

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

Lorsque j'essaie de trouver la longueur de TestField avec la fonction SQL Server LEN (), il ne compte pas les espaces de fin - par exemple:

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

Comment inclure les espaces de fin dans le résultat de longueur?

Jason Snelders
la source
1
Je pense que la vraie solution ici pourrait être que Microsoft corrige son logiciel défectueux. Votez ici: feedback.azure.com/forums/908035-sql-server/suggestions/…
QA Collective

Réponses:

125

Ceci est clairement documenté par Microsoft dans MSDN à l' adresse http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx , qui indique LEN "renvoie le nombre de caractères de l'expression de chaîne spécifiée, à l'exclusion blancs de fin ". C'est, cependant, un détail facile à manquer si vous ne vous méfiez pas.

Vous devez à la place utiliser la fonction DATALENGTH - voir http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx - qui "renvoie le nombre d'octets utilisés pour représenter une expression".

Exemple:

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
la source
52
REMARQUE: DATALENGTHvous devrez également diviser le résultat par 2 si l'expression testée est un type de caractère large (Unicode; nchar, nvarchar ou ntext), car le résultat est en octets et non en caractères .
devstuff
7
Aussi pour varcharetc. cela peut être collation à charge et même pas une division avant droite par 2 est fiable. Voir l' exemple ici
Martin Smith
18
J'utiliserais LEN(REPLACE(expr, ' ', '_')). Cela devrait fonctionner avec les chaînes varcharet nvarcharet contenant des caractères de contrôle Unicode spéciaux.
Olivier Jacot-Descombes
6
-1, DATALENGTH()ne doit pas être considéré comme un moyen alternatif de compter les caractères car il compte des octets au lieu de caractères et cela est important lors de la représentation de la même chaîne dans VARCHAR/ NVARCHAR.
binki
5
À partir de SQL Server 2012, les colonnes Unicode avec les classements de la version 100 prennent désormais en charge les paires de substitution. Cela signifie qu'un seul caractère peut utiliser jusqu'à 4 octets, provoquant l'échec de la division par deux. Voir msdn .
Frédéric
85

Vous pouvez utiliser cette astuce:

LEN (Str + 'x') - 1

Serge
la source
15
Pourriez-vous nous éclairer sur les meilleures alternatives, s'il vous plaît? Datalength n'est certainement pas.
Serge
15
Je ne suis pas du tout d'accord que l'utilisation d'une méthode incohérente (dans certains cas, vous divisez son résultat par 2 et parfois non) est une meilleure option. Peut-être y a-t-il une performance presque nulle avec ma méthode.
Serge
5
La méthode de @usr Serge est la meilleure, à mon humble avis. Simple et élégant. DATALENGTH est compliqué: type simple / double octet dépendant, collation / langue dépendante, etc.
M. TA
10
C'est la meilleure solution élégante à ce jour. Je ne me soucie pas vraiment de savoir si cela ressemble à un hack ou non (le codage n'est pas une question de sentiments), je me soucie vraiment du fait que cette solution n'a pas d'effets secondaires. Je peux changer le type de données varchar / nvarchar et cela fonctionne toujours. Bon travail.
Mike Keskinov
5
Il y a une mise en garde à cause de cet effet secondaire. Si vous travaillez avec une variable de type nvarchar (4000) et que votre variable contient une chaîne de 4000 caractères, le caractère ajouté sera ignoré et vous obtiendrez le mauvais résultat (le len de SQL qui ignore les espaces de fin, moins le 1 vous soustrayez).
hachette - fait avec SOverflow
17

J'utilise cette méthode:

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

Je préfère cela à DATALENGTH car cela fonctionne avec différents types de données, et je préfère cela à l'ajout d'un caractère à la fin car vous n'avez pas à vous soucier du cas de bord où votre chaîne est déjà à la longueur maximale.

Remarque: je testerais les performances avant de l'utiliser sur un très grand ensemble de données; bien que je viens de le tester contre 2M de lignes et ce n'était pas plus lent que LEN sans le REMPLACER ...

TTT
la source
14

"Comment puis-je inclure les espaces de fin dans le résultat de longueur?"

Vous demandez à quelqu'un de déposer une demande d'amélioration / rapport de bogue de SQL Server car presque toutes les solutions de contournement répertoriées à ce problème étonnamment simple ici présentent des lacunes ou sont inefficaces. Cela semble toujours être vrai dans SQL Server 2012. La fonction de découpage automatique peut provenir de ANSI / ISO SQL-92 mais il semble y avoir quelques trous (ou manque de les compter).

Veuillez voter pour "Ajouter un paramètre pour que LEN compte les espaces de fin" ici:

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

Lien de connexion retiré: https://connect.microsoft.com/SQLServer/feedback/details/801381

crokusek
la source
2
La datalengthsolution est encore pire à partir de SQL Server 2012, car il prend désormais en charge les paires de substitution en UTF-16, ce qui signifie qu'un caractère peut utiliser jusqu'à 4 octets. Il est vraiment temps qu'ils corrigent la lenfonction pour se conformer à ANSI, ou au moins fournissent une fonction dédiée pour compter les caractères, y compris les espaces de fin.
Frédéric
1
Le lien de rétroaction doit être utilisé davantage pour cela. Il est déconcertant que ce problème ne puisse être recherché que via Internet. J'ai passé près de 2 heures à essayer de comprendre où j'avais commis une erreur dans mon propre code avant même de considérer que la fonction LEN () était la cause de ma déconnexion.
Takophiliac
Je suis d'accord avec cela mais je devrais autoriser un paramètre à supprimer les espaces ... car cela facilite beaucoup les comparaisons de chaînes avec EF, sans avoir à vérifier s'il y a des espaces inclus lorsque l'expression iqueryable est construite.
ganjeii le
9

Il y a des problèmes avec les deux réponses les plus votées. La réponse recommandée DATALENGTHest sujette à des erreurs de programmeur. Le résultat de DATALENGTHdoit être divisé par 2 pour les NVARCHARtypes, mais pas pour les VARCHARtypes. Cela nécessite la connaissance du type dont vous obtenez la longueur, et si ce type change, vous devez changer avec diligence les endroits que vous avez utilisés DATALENGTH.

Il y a aussi un problème avec la réponse la plus votée (ce que je reconnais était ma façon préférée de le faire jusqu'à ce que ce problème me mette en cause). Si l'élément dont vous obtenez la longueur est de type NVARCHAR(4000)et qu'il contient en fait une chaîne de 4 000 caractères, SQL ignorera le caractère ajouté au lieu de convertir implicitement le résultat en NVARCHAR(MAX). Le résultat final est une longueur incorrecte. La même chose se produira avec VARCHAR (8000).

Ce que j'ai trouvé fonctionne, est presque aussi rapide que l'ancien LEN, est plus rapide que LEN(@s + 'x') - 1pour les grandes chaînes et ne suppose pas que la largeur des caractères sous-jacents est la suivante:

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

Cela obtient la longueur de données, puis divise par la longueur de données d'un seul caractère de la chaîne. L'ajout de «x» couvre le cas où la chaîne est vide (ce qui donnerait une division par zéro dans ce cas). Cela fonctionne que ce @ssoit VARCHARou NVARCHAR. Faire le caractère LEFTde 1 avant l'ajout réduit le temps lorsque la chaîne est volumineuse. Le problème avec ceci cependant, c'est que cela ne fonctionne pas correctement avec des chaînes contenant des paires de substitution.

Il y a une autre façon mentionnée dans un commentaire à la réponse acceptée, en utilisant REPLACE(@s,' ','x'). Cette technique donne la bonne réponse, mais est de quelques ordres de grandeur plus lente que les autres techniques lorsque la chaîne est grande.

Compte tenu des problèmes introduits par les paires de substitution sur toute technique qui utilise DATALENGTH, je pense que la méthode la plus sûre qui donne des réponses correctes que je connaisse est la suivante:

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

C'est plus rapide que la REPLACEtechnique, et beaucoup plus rapide avec des cordes plus longues. Fondamentalement, cette technique est la LEN(@s + 'x') - 1technique, mais avec une protection pour le cas de bord où la chaîne a une longueur de 4000 (pour nvarchar) ou 8000 (pour varchar), de sorte que la bonne réponse est donnée même pour cela. Il doit également gérer correctement les chaînes avec des paires de substitution.

hachette - fait avec SOverflow
la source
1
Malheureusement, cette réponse ne fonctionne plus pour les chaînes contenant des paires de substitution dans SQL Server 2012. L'exécution de votre opération sur N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SCdonne 4, tandis que LENdonne 3.
Douglas
9
@Douglas - Ce sont des informations utiles. Si seulement Microsoft voulait simplement nous donner une version de LEN qui n'ignore pas les espaces de fin.
hachette - fait avec SOverflow
5

Vous devez également vous assurer que vos données sont effectivement enregistrées avec les blancs de fin. Lorsque ANSI PADDING est désactivé (non par défaut):

Les blancs de fin dans les valeurs de caractère insérées dans une colonne varchar sont coupés.

Remus Rusanu
la source
3
Je pense que vous ne devriez pas désactiver ANSI PADDING car ce paramètre est obsolète. L'avoir à une valeur non standard pose de nombreux petits problèmes.
usr
4

LEN coupe les espaces de fin par défaut, donc j'ai trouvé que cela fonctionnait lorsque vous les déplacez vers l'avant

(LEN (REVERSE (TestField))

Donc si tu le voulais, tu pourrais dire

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)

N'utilisez pas cela pour diriger des espaces bien sûr.

Joey
la source
9
Maintenant, il coupe les espaces de début au lieu des espaces de fin. Même jour, problème différent :)
Ingénieur inversé
@DaveBoltman Ma suggestion est probablement encore plus compliquée, mais vous pouvez également comparer avec la longueur de TRIM.
Brian J
Cela annule le bogue où les espaces de début ne sont pas comptés au lieu des espaces de fin. Voir le code suivant: 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

Vous devez définir une fonction CLR qui renvoie le champ Longueur de la chaîne, si vous n'aimez pas la concatination de chaînes. j'utiliseLEN('x' + @string + 'x') - 2 dans mes cas d'utilisation de production.

Obratim
la source
0

Si vous n'aimez pas le en DATALENGTHraison de préoccupations n / varchar, que diriez-vous:

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

qui est juste

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

enveloppé d'une protection de division par zéro.

En divisant par la DATALENGTH d'un seul caractère, nous obtenons la longueur normalisée.

(Bien sûr, il y a toujours des problèmes avec les paires de substitution si cela pose un problème.)

dsz
la source
-4

utilisez SELECT DATALENGTH ('string')

aman6496
la source
2
vous venez de reformuler les réponses des autres 7 ans plus tôt et ne fournissez rien de nouveau ou même expliquez ce que vous répondez ou comment cela répond à cette question.
Jpsh