Функция LEN без конечных пробелов в SQL Server

109

У меня есть следующая тестовая таблица в SQL Server 2005:

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

Населен:

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

Когда я пытаюсь найти длину TestField с помощью функции SQL Server LEN (), она не учитывает конечные пробелы, например:

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

Как мне включить конечные пробелы в результат длины?

Джейсон Снелдерс
источник
1
Я думаю, что настоящим решением для Microsoft может стать исправление своего сломанного программного обеспечения. Проголосуйте здесь: feedback.azure.com/forums/908035-sql-server/suggestions/…
Коллектив QA

Ответы:

125

Это четко задокументировано Microsoft в MSDN по адресу http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx , где указано, что LEN "возвращает количество символов указанного строкового выражения, исключая конечные пробелы ". Однако эту деталь легко упустить, если вы не опасаетесь.

Вы должны вместо этого использовать функцию DATALENGTH - см http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx - который «возвращает количество байт , используемый для представления любого выражения».

Пример:

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
ПРИМЕЧАНИЕ. DATALENGTHВам также необходимо разделить результат на 2, если тестируемое выражение является типом широких символов (Unicode; nchar, nvarchar или ntext), поскольку результат выражается в байтах , а не в символах .
devstuff 08
7
Также для varcharи т.д. это может быть зависимым от сопоставления, и даже прямое деление на 2 не является надежным. См. Пример здесь
Мартин Смит
18
Я бы использовал LEN(REPLACE(expr, ' ', '_')). Это должно работать со строками varcharи nvarcharи, содержащими специальные управляющие символы Юникода.
Оливье Жако-Декомб
6
-1 DATALENGTH()не следует рассматривать как альтернативный способ подсчета символов, поскольку он считает байты вместо символов, и это имеет значение при представлении той же строки в VARCHAR/ NVARCHAR.
binki
5
Начиная с SQL Server 2012, столбцы Unicode с параметрами сортировки версии 100 теперь поддерживают суррогатные пары. Это означает, что один символ может использовать до 4 байтов, что приведет к сбою уловки деления на два. См. Msdn .
Frédéric
85

Вы можете использовать этот трюк:

LEN (Str + 'x') - 1

Serge
источник
15
Не могли бы вы рассказать нам о лучших альтернативах, пожалуйста? Длина данных точно нет.
Serge
15
Я категорически не согласен с тем, что использование несовместимого метода (в некоторых случаях вы делите результат на 2, а иногда нет) является лучшим вариантом. Может быть, мой метод почти не дает производительности.
Serge
5
Метод @usr Сержа лучший, ИМХО. Просто и элегантно. DATALENGTH сложный: однобайтовый / двухбайтовый тип, зависящий от сортировки / языка и т. Д.
г-н Т.А.
10
На данный момент это лучшее и элегантное решение. Мне все равно, ЧУВСТВУЕТ это как взлом или нет (кодирование - это не чувства), меня действительно волнует тот факт, что это решение не имеет побочных эффектов. Я могу изменить тип данных varchar / nvarchar, и он по-прежнему работает. Хорошая работа.
Майк Кескинов 07
5
Из-за этого побочного эффекта есть предостережение. Если вы работаете с переменной типа nvarchar (4000), и ваша переменная содержит строку из 4000 символов, добавленный символ будет проигнорирован, и вы получите неправильный результат (SQL len игнорирует конечные пробелы, меньше 1 вы вычитаете).
топор - сделано с SOverflow
17

Я использую такой способ:

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

Я предпочитаю DATALENGTH, потому что это работает с разными типами данных, и я предпочитаю добавлять символ в конец, потому что вам не нужно беспокоиться о граничном случае, когда ваша строка уже имеет максимальную длину.

Примечание. Я бы проверил производительность перед тем, как использовать ее на очень большом наборе данных; хотя я только что протестировал его на 2M строках, и он был не медленнее, чем LEN без REPLACE ...

TTT
источник
14

"Как мне включить конечные пробелы в результат длины?"

Вы можете попросить кого-нибудь отправить запрос на расширение SQL Server / отчет об ошибке, потому что почти все перечисленные обходные пути решения этой удивительно простой проблемы имеют некоторые недостатки или неэффективны. Это все еще кажется верным в SQL Server 2012. Функция автоматической обрезки может происходить из ANSI / ISO SQL-92, но, похоже, есть некоторые дыры (или отсутствие их подсчета).

Проголосуйте за "Добавить настройку, чтобы LEN считал конечные пробелы" здесь:

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

Ссылка на удаленное подключение: https://connect.microsoft.com/SQLServer/feedback/details/801381

крокусек
источник
2
datalengthРешение еще хуже , начиная с SQL Server 2012, так как он теперь поддерживает суррогатные пары в UTF-16, то есть персонаж может использовать до 4 байт. Пришло время исправить эту lenфункцию для соответствия ANSI или, по крайней мере, предоставить специальную функцию для подсчета символов, включая конечные пробелы.
Frédéric
1
Для этого нужно больше использовать ссылку для обратной связи. Непонятно, что эту проблему можно найти только в Интернете. Я потратил почти 2 часа, пытаясь выяснить, где я сделал ошибку в собственном коде, прежде чем даже подумал, что функция LEN () была причиной моего отключения.
Takophiliac
Я согласен с этим, но должен позволять параметру обрезать пробелы ... так как это значительно упрощает сравнение строк с EF, без необходимости проверять, включены ли пробелы при построении выражения iqueryable.
ganjeii
9

Есть проблемы с двумя ответами, получившими наибольшее количество голосов. Рекомендуемый ответ DATALENGTHподвержен ошибкам программиста. Результат DATALENGTHдолжен быть разделен на 2 для NVARCHARтипов, но не для VARCHARтипов. Это требует знания типа, длина которого вы получаете, и если этот тип изменится, вам придется старательно менять места, которые вы использовали DATALENGTH.

Также существует проблема с ответом, получившим наибольшее количество голосов (который, я признаю, был моим предпочтительным способом сделать это, пока эта проблема не укусила меня). Если объект, который вы получаете длину, относится к типу NVARCHAR(4000)и фактически содержит строку из 4000 символов, SQL будет игнорировать добавленный символ, а не неявно приводить результат к NVARCHAR(MAX). Конечный результат - неправильная длина. То же самое произойдет с VARCHAR (8000).

То, что я обнаружил, работает, почти так же быстро, как и обычное старое LEN, быстрее, чем LEN(@s + 'x') - 1для больших строк, и не предполагает, что ширина основного символа следующая:

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

Он получает длину данных, а затем делит ее на длину одного символа из строки. Добавление «x» охватывает случай, когда строка пуста (что в этом случае дает деление на ноль). Это работает независимо от того, @sесть ли VARCHARили NVARCHAR. Выполнение LEFT1 символа перед добавлением сбрит некоторое время, когда строка большая. Однако проблема заключается в том, что он некорректно работает со строками, содержащими суррогатные пары.

В комментарии к принятому ответу упоминается еще один способ, используя REPLACE(@s,' ','x'). Этот метод дает правильный ответ, но на пару порядков медленнее, чем другие методы, когда струна большая.

Учитывая проблемы, возникающие при использовании суррогатных пар при использовании любого метода DATALENGTH, я считаю, что самый безопасный метод, который дает правильные ответы, о которых я знаю, это следующий:

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

Это быстрее, чем REPLACEтехника, и намного быстрее с более длинными струнами. По сути, это техника LEN(@s + 'x') - 1, но с защитой для крайнего случая, когда строка имеет длину 4000 (для nvarchar) или 8000 (для varchar), так что даже для этого дается правильный ответ. Он также должен правильно обрабатывать строки с суррогатными парами.

топор - сделано с SOverflow
источник
1
К сожалению, этот ответ больше не работает для строк, содержащих суррогатные пары в SQL Server 2012. Выполнение операции N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SCдает 4, а LENдает 3.
Дуглас
9
@ Дуглас - Это полезная информация. Если бы только Microsoft дала нам версию LEN, которая не игнорирует конечные пробелы.
топор - сделано с SOverflow
5

Вам также необходимо убедиться, что ваши данные действительно сохранены с завершающими пробелами. Когда ANSI PADDING выключен (не по умолчанию):

Завершающие пробелы в символьных значениях, вставленных в столбец varchar, обрезаются.

Ремус Русану
источник
3
Я думаю, вам не следует отключать ANSI PADDING, поскольку этот параметр устарел. Наличие нестандартной стоимости вызывает множество мелких проблем.
usr
4

LEN по умолчанию обрезает конечные пробелы, поэтому я обнаружил, что это работает, когда вы перемещаете их на передний план.

(LEN (ОБРАТНЫЙ (TestField))

Так что, если бы вы хотели, вы могли бы сказать

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)

Конечно, не используйте это для ведущих пробелов.

Джоуи
источник
9
Теперь он урезает ведущие пробелы вместо завершающих пробелов. В тот же день, другая проблема :)
Reversed Engineer
@DaveBoltman Мое предложение, вероятно, еще более запутанное, но вы можете дополнительно сравнить с длиной TRIM.
Brian J
Это устраняет ошибку, при которой ведущие пробелы не учитываются вместо конечных пробелов. См. Следующий код: 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

Вы должны определить функцию CLR, которая возвращает поле длины строки, если вам не нравится объединение строк. Я использую LEN('x' + @string + 'x') - 2в своих производственных сценариях использования.

обратим
источник
0

Если вам не нравится DATALENGTHиз-за проблем с n / varchar, как насчет:

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

что просто

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

с защитой от деления на ноль.

Разделив на DATALENGTH одного char, мы получаем нормализованную длину.

(Конечно, по-прежнему возникают проблемы с суррогатными парами, если это вызывает беспокойство.)

dsz
источник
-4

используйте SELECT DATALENGTH ('строка')

aman6496
источник
2
Вы только что переформулировали ответы других людей 7 лет назад и не предоставили ничего нового или даже не объяснили, что вы отвечаете или как он отвечает на этот вопрос.
Jpsh