A função LEN não inclui espaços à direita no SQL Server

109

Tenho a seguinte tabela de teste no SQL Server 2005:

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

Povoado com:

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

Quando tento encontrar o comprimento de TestField com a função LEN () do SQL Server, ele não conta os espaços à direita - por exemplo:

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

Como faço para incluir os espaços à direita no resultado do comprimento?

Jason Snelders
fonte
1
Acho que a solução real aqui pode ser a Microsoft consertar seu software quebrado. Vote aqui: feedback.azure.com/forums/908035-sql-server/suggestions/…
Coletivo de controle de qualidade de

Respostas:

125

Isso está claramente documentado pela Microsoft no MSDN em http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx , que declara LEN "retorna o número de caracteres da expressão de string especificada, excluindo espaços em branco à direita ". É, no entanto, um detalhe fácil de perder, se você não for cauteloso.

Em vez disso, você precisa usar a função DATALENGTH - consulte http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx - que "retorna o número de bytes usados ​​para representar qualquer expressão".

Exemplo:

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
fonte
52
NOTA: DATALENGTHVocê também precisará dividir o resultado por 2 se a expressão testada for um tipo de caractere largo (Unicode; nchar, nvarchar ou ntext), pois o resultado está em bytes , não em caracteres .
devstuff
7
Também para varcharetc., isso pode ser dependente do agrupamento e nem mesmo uma divisão direta por 2 é confiável. Veja o exemplo aqui
Martin Smith
18
Eu usaria LEN(REPLACE(expr, ' ', '_')). Isso deve funcionar com varchare nvarchare strings contendo caracteres especiais de controle Unicode.
Olivier Jacot-Descombes
6
-1, DATALENGTH()não deve ser considerado uma forma alternativa de contar caracteres porque conta bytes em vez de caracteres e isso é importante ao representar a mesma string em VARCHAR/ NVARCHAR.
binki
5
A partir do SQL server 2012, as colunas Unicode com agrupamentos da versão 100 agora oferecem suporte a pares substitutos. Isso significa que um único caractere pode usar até 4 bytes, fazendo com que o truque de divisão por dois falhe. Veja msdn .
Frédéric
85

Você pode usar este truque:

LEN (Str + 'x') - 1

Sarja
fonte
15
Você poderia nos esclarecer com as melhores alternativas, por favor? Datalength com certeza não é.
Serge,
15
Discordo veementemente que usar um método inconsistente (em alguns casos você divide seu resultado por 2 e às vezes não) é uma opção melhor. Talvez haja uma queda de desempenho quase zero com o meu método.
Serge
5
O método de @usr Serge é o melhor, IMHO. Simples e elegante. DATALENGTH é complicado: dependente do tipo de byte simples / duplo, dependente do agrupamento / idioma, etc.
Sr. TA
10
Esta é a melhor e elegante solução até agora. Eu realmente não me importo se PARECE um hack ou não (codificação não é sobre sentimentos), eu realmente me importo com o fato de que esta solução não tem efeitos colaterais. Posso alterar o tipo de dados varchar / nvarchar e ainda funciona. Bom trabalho.
Mike Keskinov
5
Há uma advertência por causa desse efeito colateral. Se você estiver trabalhando com uma variável do tipo nvarchar (4000) e sua variável contiver uma string de 4000 caracteres, o caractere adicionado será ignorado e você obterá o resultado errado (len do SQL que ignora os espaços à direita, menos o 1 você subtrai).
machado - feito com SOverflow
17

Eu uso este método:

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

Eu prefiro isso em vez de DATALENGTH porque funciona com diferentes tipos de dados, e eu prefiro adicionar um caractere ao final porque você não precisa se preocupar com o caso extremo em que sua string já está no comprimento máximo.

Nota: Eu testaria o desempenho antes de usá-lo em um conjunto de dados muito grande; embora eu o tenha testado em 2 milhões de linhas e não tenha sido mais lento do que LEN sem REPLACE ...

TTT
fonte
14

"Como faço para incluir os espaços à direita no resultado do comprimento?"

Você pede a alguém para registrar uma solicitação de aprimoramento / relatório de bug do SQL Server porque quase todas as soluções alternativas listadas para esse problema incrivelmente simples aqui têm alguma deficiência ou são ineficientes. Isso ainda parece ser verdade no SQL Server 2012. O recurso de corte automático pode se originar do ANSI / ISO SQL-92, mas parece haver algumas lacunas (ou a falta de contá-las).

Vote em "Adicionar configuração para que LEN conte os espaços em branco à direita" aqui:

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

Retired Connect link: https://connect.microsoft.com/SQLServer/feedback/details/801381

crokusek
fonte
2
A datalengthsolução é ainda pior a partir do SQL server 2012, pois agora ele oferece suporte a pares substitutos em UTF-16, o que significa que um caractere pode usar até 4 bytes. Está na hora de eles corrigirem a lenfunção de conformidade com ANSI, ou pelo menos fornecer uma função dedicada para contagem de caracteres incluindo espaços à direita.
Frédéric
1
O link de feedback precisa ser mais usado para isso. É desconcertante que esse problema só possa ser pesquisado pela internet. Passei quase 2 horas tentando descobrir onde cometi um erro em meu próprio código antes mesmo de considerar que a função LEN () foi a causa da minha desconexão.
Takophiliac
Eu concordo com isso, mas deve permitir que um parâmetro elimine os espaços em branco ... pois torna as comparações de strings com EF muito mais fáceis, não tendo que verificar se há espaços em branco incluídos quando a expressão iqueryable é construída.
ganjeii
9

Existem problemas com as duas respostas mais votadas. A resposta recomendando DATALENGTHestá sujeita a erros do programador. O resultado de DATALENGTHdeve ser dividido por 2 para NVARCHARtipos, mas não para VARCHARtipos. Isso requer conhecimento do tipo do qual você está obtendo o comprimento e, se esse tipo mudar, você terá que alterar diligentemente os locais que usou DATALENGTH.

Também há um problema com a resposta mais votada (que, admito, era minha maneira preferida de fazer isso até que esse problema me mordesse). Se o que você está obtendo com o comprimento for do tipo NVARCHAR(4000)e, na verdade, contiver uma string de 4.000 caracteres, o SQL irá ignorar o caractere anexado em vez de lançar implicitamente o resultado NVARCHAR(MAX). O resultado final é um comprimento incorreto. O mesmo acontecerá com VARCHAR (8000).

O que eu descobri que funciona, é quase tão rápido quanto o normal LEN, é mais rápido do que LEN(@s + 'x') - 1para strings grandes e não assume que a largura do caractere subjacente seja a seguinte:

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

Isso obtém o comprimento de dados e, em seguida, divide pelo comprimento de dados de um único caractere da string. O apêndice de 'x' cobre o caso em que a string está vazia (o que resultaria em uma divisão por zero nesse caso). Isso funciona se @sé VARCHARou NVARCHAR. Fazer o LEFTde 1 caractere antes do acréscimo corta algum tempo quando a string é grande. O problema com isso, porém, é que não funciona corretamente com strings contendo pares substitutos.

Há outra forma mencionada em um comentário para a resposta aceita, usando REPLACE(@s,' ','x'). Essa técnica dá a resposta correta, mas é algumas ordens de magnitude mais lenta do que as outras técnicas quando a corda é grande.

Dados os problemas introduzidos por pares substitutos em qualquer técnica que use DATALENGTH, acho que o método mais seguro que dá respostas corretas que conheço é o seguinte:

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

Isso é mais rápido do que a REPLACEtécnica e muito mais rápido com cordas mais longas. Basicamente esta técnica é a LEN(@s + 'x') - 1técnica, mas com proteção para o caso extremo onde a string tem um comprimento de 4000 (para nvarchar) ou 8000 (para varchar), de forma que a resposta correta é dada até mesmo para isso. Ele também deve lidar com strings com pares substitutos corretamente.

machado - feito com SOverflow
fonte
1
Infelizmente, essa resposta não funciona mais para strings contendo pares substitutos no SQL Server 2012. Executar sua operação em N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SCdá 4, enquanto LENdá 3.
Douglas
9
@Douglas - Essa é uma informação útil. Se ao menos a Microsoft nos desse uma versão do LEN que não ignore os espaços à direita.
machado - feito com SOverflow
5

Você também precisa garantir que seus dados sejam realmente salvos com os espaços em branco à direita. Quando ANSI PADDING está OFF (não padrão):

Os espaços em branco à direita em valores de caractere inseridos em uma coluna varchar são aparados.

Remus Rusanu
fonte
3
Acho que você não deve desativar o ANSI PADDING, pois essa configuração é obsoleta. Ter um valor fora do padrão causa muitos pequenos problemas.
usr
4

LEN corta espaços à direita por padrão, então descobri que isso funcionou conforme você os move para a frente

(LEN (REVERSE (TestField))

Então, se você quiser, você pode dizer

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ão use isso para espaços iniciais, é claro.

Joey
fonte
9
Agora, ele corta espaços à esquerda em vez de espaços à direita. Mesmo dia, problema diferente :)
Engenheiro
@DaveBoltman Minha sugestão é provavelmente mais complicada ainda, mas você também pode comparar com o comprimento TRIM'ed.
Brian J
Isso reverte o bug onde os espaços à esquerda não são contados em vez dos espaços à direita. Veja o seguinte 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

Você deve definir uma função CLR que retorna o campo Comprimento da string, se você não gosta de concatinação de string. Eu uso LEN('x' + @string + 'x') - 2em meus casos de uso de produção.

obratim
fonte
0

Se você não gosta de DATALENGTHdevido a questões de n / varchar, que tal:

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

que é apenas

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

embalado com proteção de divisão por zero.

Dividindo pelo DATALENGTH de um único caractere, obtemos o comprimento normalizado.

(Claro, ainda tem problemas com pares substitutos, se isso for uma preocupação.)

DSZ
fonte
-4

use SELECT DATALENGTH ('string')

aman6496
fonte
2
você apenas reafirmou as respostas dos outros de 7 anos antes e não forneceu nada de novo ou mesmo explicou o que sua resposta faz ou como ela responde a essa pergunta.
Jpsh de