Лучшие методы для обрезки лидирующих нулей в SQL Server? - PullRequest
134 голосов
/ 19 марта 2009

Я уже некоторое время пользуюсь этим :

SUBSTRING(str_col, PATINDEX('%[^0]%', str_col), LEN(str_col))

Однако недавно я обнаружил проблему со столбцами со всеми символами «0», такими как «00000000», поскольку он никогда не находит совпадения, отличного от «0».

Альтернативная техника, которую я видел, состоит в использовании TRIM:

REPLACE(LTRIM(REPLACE(str_col, '0', ' ')), ' ', '0')

Это имеет проблему, если есть встроенные пробелы, потому что они будут превращены в «0», когда пробелы возвращаются в «0».

Я пытаюсь избежать скалярного UDF. В SQL Server 2005 я обнаружил множество проблем с производительностью пользовательских функций.

Ответы [ 13 ]

236 голосов
/ 19 марта 2009
SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col))
30 голосов
/ 19 марта 2009

Почему бы вам просто не привести значение к INTEGER, а затем вернуться к VARCHAR?

SELECT  CAST(CAST('000000000' AS INTEGER) AS VARCHAR)

--------
       0
13 голосов
/ 16 февраля 2014

Другие ответы здесь, чтобы не принимать во внимание, если у вас есть все нули (или даже один ноль).
Некоторые всегда устанавливают по умолчанию пустую строку на ноль, что неправильно, если предполагается, что она остается пустой.
Перечитайте оригинальный вопрос. Это отвечает тому, что хочет спрашивающий.

Решение № 1:

--This example uses both Leading and Trailing zero's.
--Avoid losing those Trailing zero's and converting embedded spaces into more zeros.
--I added a non-whitespace character ("_") to retain trailing zero's after calling Replace().
--Simply remove the RTrim() function call if you want to preserve trailing spaces.
--If you treat zero's and empty-strings as the same thing for your application,
--  then you may skip the Case-Statement entirely and just use CN.CleanNumber .
DECLARE @WackadooNumber VarChar(50) = ' 0 0123ABC D0 '--'000'--
SELECT WN.WackadooNumber, CN.CleanNumber,
       (CASE WHEN WN.WackadooNumber LIKE '%0%' AND CN.CleanNumber = '' THEN '0' ELSE CN.CleanNumber END)[AllowZero]
 FROM (SELECT @WackadooNumber[WackadooNumber]) AS WN
 OUTER APPLY (SELECT RTRIM(RIGHT(WN.WackadooNumber, LEN(LTRIM(REPLACE(WN.WackadooNumber + '_', '0', ' '))) - 1))[CleanNumber]) AS CN
--Result: "123ABC D0"

Решение № 2 (с образцами данных):

SELECT O.Type, O.Value, Parsed.Value[WrongValue],
       (CASE WHEN CHARINDEX('0', T.Value)  > 0--If there's at least one zero.
              AND LEN(Parsed.Value) = 0--And the trimmed length is zero.
             THEN '0' ELSE Parsed.Value END)[FinalValue],
       (CASE WHEN CHARINDEX('0', T.Value)  > 0--If there's at least one zero.
              AND LEN(Parsed.TrimmedValue) = 0--And the trimmed length is zero.
             THEN '0' ELSE LTRIM(RTRIM(Parsed.TrimmedValue)) END)[FinalTrimmedValue]
  FROM 
  (
    VALUES ('Null', NULL), ('EmptyString', ''),
           ('Zero', '0'), ('Zero', '0000'), ('Zero', '000.000'),
           ('Spaces', '    0   A B C '), ('Number', '000123'),
           ('AlphaNum', '000ABC123'), ('NoZero', 'NoZerosHere')
  ) AS O(Type, Value)--O is for Original.
  CROSS APPLY
  ( --This Step is Optional.  Use if you also want to remove leading spaces.
    SELECT LTRIM(RTRIM(O.Value))[Value]
  ) AS T--T is for Trimmed.
  CROSS APPLY
  ( --From @CadeRoux's Post.
    SELECT SUBSTRING(O.Value, PATINDEX('%[^0]%', O.Value + '.'), LEN(O.Value))[Value],
           SUBSTRING(T.Value, PATINDEX('%[^0]%', T.Value + '.'), LEN(T.Value))[TrimmedValue]
  ) AS Parsed

Результаты:

MikeTeeVee_SQL_Server_Remove_Leading_Zeros

Резюме:

Вы можете использовать то, что у меня есть выше, для одноразового удаления начальных нулей.
Если вы планируете многократно использовать его, поместите его в функцию встроенной таблицы (ITVF).
Ваши опасения по поводу проблем с производительностью в UDF понятны.
Однако эта проблема относится только к функциям All-Scalar-Functions и Multi-Statement-Table-Functions.
Использование ITVF прекрасно.

У меня та же проблема с нашей сторонней базой данных.
С помощью буквенно-цифровых полей многие вводятся без пробелов, черт возьми!
Это делает невозможным объединение без очистки недостающих начальных нулей.

Вывод:

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

SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF(' 0A10  ', ''))), 10)--0000000A10
SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF('', ''))), 10)--NULL --When Blank.
5 голосов
/ 19 марта 2009

Вместо пробела замените 0 на «редкий» символ пробела, которого обычно не должно быть в тексте столбца. Перевод строки, вероятно, достаточно хорош для такого столбца. Затем вы можете использовать LTrim в обычном режиме и снова заменить специальный символ на 0.

3 голосов
/ 03 марта 2011

Следующая строка вернет '0', если строка полностью состоит из нулей:

CASE WHEN SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col)) = '' THEN '0' ELSE SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col)) END AS str_col
2 голосов
/ 12 апреля 2016

приведение (значение как int) всегда будет работать, если строка является числом

2 голосов
/ 19 июля 2013

Это хорошая функция ....

DROP FUNCTION [dbo].[FN_StripLeading]
GO
CREATE FUNCTION [dbo].[FN_StripLeading] (@string VarChar(128), @stripChar VarChar(1))
RETURNS VarChar(128)
AS
BEGIN
-- /503576/luchshie-metody-dlya-obrezki-lidiruyschih-nulei-v-sql-server
    DECLARE @retVal VarChar(128),
            @pattern varChar(10)
    SELECT @pattern = '%[^'+@stripChar+']%'
    SELECT @retVal = CASE WHEN SUBSTRING(@string, PATINDEX(@pattern, @string+'.'), LEN(@string)) = '' THEN @stripChar ELSE SUBSTRING(@string, PATINDEX(@pattern, @string+'.'), LEN(@string)) END
    RETURN (@retVal)
END
GO
GRANT EXECUTE ON [dbo].[FN_StripLeading] TO PUBLIC
1 голос
/ 26 октября 2018

Если вы используете Snowflake SQL, можете использовать это:

ltrim(str_col,'0')

Функция ltrim удаляет все экземпляры назначенного набора символов с левой стороны.

Таким образом, ltrim (str_col, '0') в '00000008A' вернет '8A'

И rtrim (str_col, '0.') В '125,00 $' вернет '$ 125'

1 голос
/ 09 декабря 2016
replace(ltrim(replace(Fieldname.TableName, '0', '')), '', '0')

Предложение Томаса Г. сработало для наших нужд.

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

1 голос
/ 04 августа 2016

Моя версия - адаптация работы Арво, с добавлением немного больше, чтобы обеспечить два других случая.

1) Если у нас есть все 0, мы должны вернуть цифру 0.

2) Если у нас есть пробел, мы все равно должны вернуть пустой символ.

CASE 
    WHEN PATINDEX('%[^0]%', str_col + '.') > LEN(str_col) THEN RIGHT(str_col, 1) 
    ELSE SUBSTRING(str_col, PATINDEX('%[^0]%', str_col + '.'), LEN(str_col))
 END
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...