Funzione LEN che non include gli spazi finali in SQL Server


109

Ho la seguente tabella di test in SQL Server 2005:

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

Popolato 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

Quando provo a trovare la lunghezza di TestField con la funzione LEN () di SQL Server non conta gli spazi finali, ad esempio:

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

Come includo gli spazi finali nel risultato della lunghezza?


1
Penso che la vera soluzione qui potrebbe essere per Microsoft riparare il loro software rotto. Vota qui: feedback.azure.com/forums/908035-sql-server/suggestions/…
QA Collective

Risposte:


125

Ciò è chiaramente documentato da Microsoft in MSDN all'indirizzo http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx , che afferma LEN "restituisce il numero di caratteri dell'espressione stringa specificata, escludendo spazi vuoti finali ". Tuttavia, è un dettaglio facile da perdere se non sei diffidente.

È necessario invece utilizzare la funzione DATALENGTH - vedere http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx - che "restituisce il numero di byte utilizzati per rappresentare qualsiasi espressione".

Esempio:

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

52
NOTA: poiché DATALENGTHdovrai anche dividere il risultato per 2 se l'espressione da testare è un tipo di carattere ampio (Unicode; nchar, nvarchar o ntext), poiché il risultato è in byte , non in caratteri .
devstuff

7
Anche per varcharecc. Questo può dipendere dalle regole di confronto e nemmeno una semplice divisione per 2 è affidabile. Vedi esempio qui
Martin Smith

18
Io userei LEN(REPLACE(expr, ' ', '_')). Questo dovrebbe funzionare con varchare nvarchare stringhe contenenti caratteri di controllo Unicode speciali.
Olivier Jacot-Descombes

6
-1, DATALENGTH()non dovrebbe essere considerato un modo alternativo per contare i caratteri perché conta i byte invece dei caratteri e questo è importante quando rappresenta la stessa stringa in VARCHAR/ NVARCHAR.
binki

5
A partire da SQL Server 2012, le colonne Unicode con le regole di confronto della versione 100 ora supportano le coppie surrogate. Ciò significa che un singolo carattere può utilizzare fino a 4 byte, facendo fallire il trucco di divisione per due. Vedi msdn .
Frédéric

85

Puoi usare questo trucco:

LEN (Str + 'x') - 1


15
Potresti illuminarci con le alternative migliori, per favore? Datalength sicuramente non lo è.
Serge

15
Non sono assolutamente d'accordo sul fatto che l'utilizzo di un metodo incoerente (in alcuni casi si divide il risultato per 2 e talvolta no) sia un'opzione migliore. Forse c'è un calo di prestazioni vicino allo zero con il mio metodo però.
Serge

5
Il metodo di @usr Serge è il migliore, IMHO. Semplice ed elegante. DATALENGTH è complicato: dipendente dal tipo di byte singolo / doppio, dipendente dalle regole di confronto / dalla lingua, ecc.
Mr. TA

10
Questa è la soluzione migliore ed elegante finora. Non mi interessa davvero se SENTI come un hack o no (la codifica non riguarda i sentimenti), mi interessa davvero il fatto che questa soluzione non abbia effetti collaterali. Posso cambiare il tipo di dati varchar / nvarchar e funziona ancora. Buon lavoro.
Mike Keskinov

5
C'è un avvertimento a causa di questo effetto collaterale. Se stai lavorando con una variabile di tipo nvarchar (4000) e la tua variabile contiene una stringa di 4000 caratteri, il carattere aggiunto verrà ignorato e otterrai il risultato sbagliato (len di SQL che ignora gli spazi finali, meno 1 si sottrae).
ascia - fatto con SOverflow

17

Uso questo metodo:

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

Lo preferisco a DATALENGTH perché funziona con diversi tipi di dati, e lo preferisco rispetto all'aggiunta di un carattere alla fine perché non devi preoccuparti del caso limite in cui la tua stringa è già alla lunghezza massima.

Nota: testerei le prestazioni prima di utilizzarlo su un set di dati molto grande; anche se l'ho appena testato su 2 milioni di righe e non era più lento di LEN senza REPLACE ...


14

"Come includo gli spazi finali nel risultato della lunghezza?"

Puoi chiedere a qualcuno di presentare una richiesta di miglioramento di SQL Server / una segnalazione di bug perché quasi tutte le soluzioni alternative elencate a questo problema incredibilmente semplice qui presentano alcune lacune o sono inefficienti. Questo sembra essere ancora vero in SQL Server 2012. La funzione di taglio automatico potrebbe derivare da ANSI / ISO SQL-92 ma sembra che ci siano alcuni buchi (o la mancanza di conteggio).

Si prega di votare "Aggiungi impostazione in modo che LEN conti gli spazi vuoti finali" qui:

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

Collegamento Connect ritirato: https://connect.microsoft.com/SQLServer/feedback/details/801381


2
La datalengthsoluzione è ancora peggiore a partire da SQL Server 2012, poiché ora supporta le coppie surrogate in UTF-16, il che significa che un carattere può utilizzare fino a 4 byte. È davvero ora che aggiustino la lenfunzione per la conformità con ANSI, o almeno forniscano una funzione dedicata per il conteggio dei caratteri inclusi gli spazi finali.
Frédéric

1
Il link di feedback deve essere utilizzato di più per questo. È sconcertante che questo problema possa essere cercato solo tramite Internet. Ho passato quasi 2 ore cercando di capire dove avevo commesso un errore nel mio codice prima ancora di considerare che la funzione LEN () era la causa della mia disconnessione.
Takophiliac

Sono d'accordo con questo ma dovrebbe consentire a un parametro di tagliare gli spazi vuoti .. poiché rende molto più semplici i confronti delle stringhe con EF, non dovendo controllare se sono inclusi spazi bianchi quando viene creata l'espressione iqueryable.
ganjeii

9

Ci sono problemi con le due risposte più votate. La risposta che consiglia DATALENGTHè soggetta a errori del programmatore. Il risultato di DATALENGTHdeve essere diviso per 2 per i NVARCHARtipi, ma non per i VARCHARtipi. Ciò richiede la conoscenza del tipo di cui stai ottenendo la lunghezza e, se quel tipo cambia, devi cambiare diligentemente i luoghi che hai usato DATALENGTH.

C'è anche un problema con la risposta più votata (che ammetto era il mio modo preferito per farlo fino a quando questo problema non mi ha colpito). Se l'oggetto di cui stai ottenendo la lunghezza è di tipo NVARCHAR(4000)e contiene effettivamente una stringa di 4000 caratteri, SQL ignorerà il carattere aggiunto anziché eseguire il cast implicito del risultato NVARCHAR(MAX). Il risultato finale è una lunghezza errata. La stessa cosa accadrà con VARCHAR (8000).

Quello che ho trovato funziona, è veloce quasi quanto il vecchio LEN, è più veloce rispetto LEN(@s + 'x') - 1alle stringhe di grandi dimensioni e non presume che la larghezza del carattere sottostante sia la seguente:

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

Questo ottiene la lunghezza dei dati e quindi la divide per la lunghezza dei dati di un singolo carattere dalla stringa. L'appendice di "x" copre il caso in cui la stringa è vuota (che in quel caso darebbe una divisione per zero). Questo funziona se @sè VARCHARo NVARCHAR. L'esecuzione LEFTdi 1 carattere prima dell'appendice riduce il tempo quando la stringa è grande. Il problema con questo però è che non funziona correttamente con stringhe contenenti coppie surrogate.

C'è un altro modo menzionato in un commento alla risposta accettata, usando REPLACE(@s,' ','x'). Questa tecnica fornisce la risposta corretta, ma è un paio di ordini di grandezza più lenta rispetto alle altre tecniche quando la corda è grande.

Dati i problemi introdotti dalle coppie surrogate su qualsiasi tecnica che utilizzi DATALENGTH, penso che il metodo più sicuro che dia risposte corrette che conosco sia il seguente:

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

Questo è più veloce della REPLACEtecnica e molto più veloce con corde più lunghe. Fondamentalmente questa tecnica è la LEN(@s + 'x') - 1tecnica, ma con protezione per il caso limite in cui la stringa ha una lunghezza di 4000 (per nvarchar) o 8000 (per varchar), in modo che la risposta corretta sia data anche per questo. Inoltre, dovrebbe gestire correttamente le stringhe con coppie surrogate.


1
Sfortunatamente, questa risposta non funziona più per le stringhe contenenti coppie surrogate in SQL Server 2012. L'esecuzione dell'operazione su N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SCdà 4, mentre LENdà 3.
Douglas

9
@Douglas - Questa è un'informazione utile. Se solo Microsoft ci desse una versione di LEN che non ignora gli spazi finali.
accetta - fatto con SOverflow il

5

Devi anche assicurarti che i tuoi dati siano effettivamente salvati con gli spazi finali. Quando ANSI PADDING è OFF (non predefinito):

Gli spazi finali nei valori dei caratteri inseriti in una colonna varchar vengono tagliati.


3
Penso che non dovresti disattivare ANSI PADDING poiché questa impostazione è obsoleta. Averlo a un valore non standard causa molti piccoli problemi.
usr

4

LEN taglia gli spazi finali per impostazione predefinita, quindi ho scoperto che ha funzionato mentre li sposti in primo piano

(LEN (REVERSE (TestField))

Quindi, se lo volessi, potresti 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)

Ovviamente non usarlo per gli spazi principali.


9
Ora TRIM leader spazi invece di spazi finali. Lo stesso giorno, problema diverso :)
Ingegnere inverso

@ DaveBoltman Il mio suggerimento è probabilmente ancora più complesso, ma puoi anche confrontare con la lunghezza TRIM.
Brian J

Questo inverte il bug in cui gli spazi iniziali non vengono conteggiati invece degli spazi finali. Vedere il seguente codice: 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

È necessario definire una funzione CLR che restituisca il campo Lunghezza della stringa, se non ti piace la concatinazione di stringhe. Uso LEN('x' + @string + 'x') - 2nei miei casi d'uso di produzione.


0

Se non ti piace la DATALENGTHcausa di problemi di n / varchar, che ne dici di:

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

che è giusto

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

avvolto con protezione divide per zero.

Dividendo per DATALENGTH di un singolo carattere, otteniamo la lunghezza normalizzata.

(Ovviamente, problemi con le coppie surrogate se questo è un problema.)


-4

usa SELECT DATALENGTH ('stringa')


2
hai appena riaffermato le risposte degli altri di 7 anni prima e non hai fornito nulla di nuovo o addirittura spiegato cosa fai o come risponde a quella domanda.
Jpsh
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.