La función LEN no incluye espacios finales en SQL Server

109

Tengo la siguiente tabla de prueba en SQL Server 2005:

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

Poblado con:

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

Cuando trato de encontrar la longitud de TestField con la función SQL Server LEN (), no cuenta los espacios finales, por ejemplo:

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

¿Cómo incluyo los espacios finales en el resultado de la longitud?

Jason Snelders
fuente
1
Creo que la verdadera solución aquí podría ser que Microsoft repare su software roto. Vote aquí: feedback.azure.com/forums/908035-sql-server/suggestions/…
QA Collective

Respuestas:

125

Esto está claramente documentado por Microsoft en MSDN en http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx , que dice LEN "devuelve el número de caracteres de la expresión de cadena especificada, excluyendo espacios en blanco finales ". Sin embargo, es un detalle fácil de pasar por alto si no tiene cuidado.

En su lugar, debe utilizar la función DATALENGTH; consulte http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx , que "devuelve el número de bytes utilizados para representar cualquier expresión".

Ejemplo:

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
fuente
52
NOTA: DATALENGTHTambién deberá dividir el resultado entre 2 si la expresión que se está probando es un tipo de carácter ancho (Unicode; nchar, nvarchar o ntext), ya que el resultado está en bytes , no en caracteres .
devstuff
7
También para varcharetc., esto puede depender de la clasificación y ni siquiera una división directa entre 2 es confiable. Vea el ejemplo aquí
Martin Smith
18
Yo usaría LEN(REPLACE(expr, ' ', '_')). Esto debería funcionar con cadenas varchary nvarchary que contienen caracteres de control unicode especiales.
Olivier Jacot-Descombes
6
-1, DATALENGTH()no debe considerarse una forma alternativa de contar caracteres porque cuenta bytes en lugar de caracteres y esto es importante cuando se representa la misma cadena en VARCHAR/ NVARCHAR.
binki
5
A partir de SQL Server 2012, las columnas Unicode con intercalaciones de la versión 100 ahora admiten pares sustitutos. Esto significa que un solo carácter puede utilizar hasta 4 bytes, lo que hace que falle el truco de división por dos. Ver msdn .
Frédéric
85

Puedes usar este truco:

LEN (Str + 'x') - 1

Sarga
fuente
15
¿Podría iluminarnos con las mejores alternativas, por favor? Datalength seguro que no lo es.
Serge
15
Estoy totalmente en desacuerdo con que usar un método inconsistente (en algunos casos divides su resultado entre 2 y otras no) sea una mejor opción. Sin embargo, tal vez haya un impacto de rendimiento cercano a cero con mi método.
Serge
5
El método de @usr Serge es el mejor, en mi humilde opinión. Simple y elegante. DATALENGTH es complicado: depende del tipo de byte simple / doble, depende de la clasificación / del idioma, etc.
Sr. TA
10
Esta es la mejor y elegante solución hasta ahora. Realmente no me importa si se SIENTE como un truco o no (la codificación no se trata de sentimientos), realmente me importa el hecho de que esta solución no tiene efectos secundarios. Puedo cambiar el tipo de datos varchar / nvarchar y todavía funciona. Buen trabajo.
Mike Keskinov
5
Hay una advertencia debido a este efecto secundario. Si está trabajando con una variable de tipo nvarchar (4000) y su variable contiene una cadena de 4000 caracteres, el carácter agregado se ignorará y obtendrá el resultado incorrecto (la longitud de SQL que ignora los espacios finales, menos el 1 restas).
hacha - hecho con SOverflow
17

Yo uso este método:

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

Prefiero esto sobre DATALENGTH porque funciona con diferentes tipos de datos, y lo prefiero a agregar un carácter al final porque no tiene que preocuparse por el caso de borde donde su cadena ya está en la longitud máxima.

Nota: probaría el rendimiento antes de usarlo en un conjunto de datos muy grande; aunque solo lo probé contra 2M filas y no fue más lento que LEN sin REPLACE ...

TTT
fuente
14

"¿Cómo incluyo los espacios finales en el resultado de longitud?"

Usted consigue que alguien presente un informe de error / solicitud de mejora de SQL Server porque casi todas las soluciones enumeradas para este problema increíblemente simple aquí tienen alguna deficiencia o son ineficientes. Esto todavía parece ser cierto en SQL Server 2012. La función de recorte automático puede provenir de ANSI / ISO SQL-92 pero parece haber algunos agujeros (o falta de contarlos).

Vota "Agregar configuración para que LEN cuente los espacios en blanco finales" aquí:

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

Enlace de conexión retirado: https://connect.microsoft.com/SQLServer/feedback/details/801381

crokusek
fuente
2
La datalengthsolución es aún peor a partir de SQL Server 2012, ya que ahora admite pares sustitutos en UTF-16, lo que significa que un carácter puede usar hasta 4 bytes. Realmente es hora de que arreglen la lenfunción para cumplir con ANSI, o al menos proporcionen una función dedicada para contar caracteres, incluidos los espacios finales.
Frédéric
1
El enlace de comentarios debe usarse más para esto. Es desconcertante que este problema solo se pueda buscar a través de Internet. Pasé casi 2 horas tratando de averiguar dónde había cometido un error en mi propio código antes incluso de considerar que la función LEN () era la causa de mi desconexión.
Takophiliac
Estoy de acuerdo con esto, pero debería permitir un parámetro para recortar los espacios en blanco ... ya que hace que las comparaciones de cadenas con EF sean mucho más fáciles, sin tener que verificar si hay espacios en blanco incluidos cuando se construye la expresión iqueryable.
ganjeii
9

Hay problemas con las dos respuestas más votadas. La respuesta recomendada DATALENGTHes propensa a errores del programador. El resultado de DATALENGTHdebe dividirse por 2 para NVARCHARtipos, pero no para VARCHARtipos. Esto requiere conocimiento del tipo que está obteniendo, y si ese tipo cambia, debe cambiar diligentemente los lugares que utilizó DATALENGTH.

También hay un problema con la respuesta más votada (que admito que era mi forma preferida de hacerlo hasta que este problema me mordió). Si lo que obtiene la longitud es de tipo NVARCHAR(4000), y en realidad contiene una cadena de 4000 caracteres, SQL ignorará el carácter agregado en lugar de emitir implícitamente el resultado NVARCHAR(MAX). El resultado final es una longitud incorrecta. Lo mismo ocurrirá con VARCHAR (8000).

Lo que he encontrado funciona, es casi tan rápido como lo viejo LEN, es más rápido que LEN(@s + 'x') - 1para cadenas grandes y no asume que el ancho del carácter subyacente es el siguiente:

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

Esto obtiene la longitud de datos y luego se divide por la longitud de datos de un solo carácter de la cadena. El agregado de 'x' cubre el caso donde la cadena está vacía (lo que daría una división por cero en ese caso). Esto funciona si @ses VARCHARo NVARCHAR. Hacer el LEFTde 1 carácter antes de agregar se afeita en algún momento cuando la cadena es grande. Sin embargo, el problema con esto es que no funciona correctamente con cadenas que contienen pares suplentes.

Hay otra forma mencionada en un comentario para la respuesta aceptada, usando REPLACE(@s,' ','x'). Esa técnica da la respuesta correcta, pero es un par de órdenes de magnitud más lenta que las otras técnicas cuando la cuerda es grande.

Dados los problemas introducidos por los pares sustitutos en cualquier técnica que utilice DATALENGTH, creo que el método más seguro que da respuestas correctas que conozco es el siguiente:

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

Esto es más rápido que la REPLACEtécnica y mucho más rápido con cuerdas más largas. Básicamente, esta técnica es la LEN(@s + 'x') - 1técnica, pero con protección para el caso de borde donde la cadena tiene una longitud de 4000 (para nvarchar) u 8000 (para varchar), de modo que la respuesta correcta se da incluso para eso. También debe manejar correctamente las cadenas con pares sustitutos.

hacha - hecho con SOverflow
fuente
1
Desafortunadamente, esta respuesta ya no funciona para cadenas que contienen pares sustitutos en SQL Server 2012. Ejecutar su operación en N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SCda 4, mientras que LENda 3.
Douglas
9
@Douglas - Esa es información útil. Si tan solo Microsoft nos diera una versión de LEN que no ignora los espacios finales.
hacha - hecho con SOverflow
5

También debe asegurarse de que sus datos se guarden realmente con los espacios en blanco finales. Cuando ANSI PADDING está APAGADO (no predeterminado):

Los espacios en blanco finales en los valores de carácter insertados en una columna varchar se recortan.

Remus Rusanu
fuente
3
Creo que no debería desactivar ANSI PADDING ya que esta configuración es obsoleta. Tenerlo a un valor no estándar causa muchos pequeños problemas.
usr
4

LEN corta los espacios finales de forma predeterminada, así que encontré que esto funcionó a medida que los mueve al frente

(LEN (REVERSE (TestField))

Entonces, si quisieras, podrías decir

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)

No use esto para espacios principales, por supuesto.

Joey
fuente
9
Ahora recorta los espacios iniciales en lugar de los espacios finales. El mismo día, problema diferente :)
Ingeniero
@DaveBoltman Mi sugerencia es probablemente aún más complicada, pero también podría comparar con la longitud TRIM'ed.
Brian J
Esto revierte el error donde los espacios iniciales no se cuentan en lugar de los espacios finales. Ver el siguiente código: 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

Debe definir una función CLR que devuelva el campo Longitud de cadena, si no le gusta la concatenación de cadenas. Yo uso LEN('x' + @string + 'x') - 2en mis casos de uso de producción.

obratim
fuente
0

Si no le gustan las DATALENGTHpreocupaciones debido a n / varchar, ¿qué tal:

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

que es solo

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

envuelto con protección de división por cero.

Al dividir por DATALENGTH de un solo carácter, obtenemos la longitud normalizada.

(Por supuesto, todavía hay problemas con los pares sustitutos si eso es una preocupación).

dsz
fuente
-4

use SELECT DATALENGTH ('cadena')

aman6496
fuente
2
simplemente repitió las respuestas de otros de 7 años antes y no proporcionó nada nuevo o incluso explicó lo que responde o cómo responde esa pregunta.
Jpsh