Функция 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


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


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), так что даже для этого дается правильный ответ. Он также должен правильно обрабатывать строки с суррогатными парами.


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, мы получаем нормализованную длину.

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


-4

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


2
Вы только что переформулировали ответы других людей 7 лет назад и не предоставили ничего нового или даже не объяснили, что вы отвечаете или как он отвечает на этот вопрос.
Jpsh
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.