更好的修剪SQL Server中前导零的技术?

时间:2009-03-19 14:16:23

标签: sql sql-server sql-server-2005 tsql string

我已经使用this一段时间了:

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中发现了很多UDF的性能问题。

17 个答案:

答案 0 :(得分:235)

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

答案 1 :(得分:29)

为什么不直接将值转换为INTEGER,然后又转回VARCHAR

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

--------
       0

答案 2 :(得分:13)

如果你有全零(或甚至一个零),这里的其他答案不予考虑 有些人总是将空字符串默认为零,这在它应该保持空白时是错误的 重新阅读原始问题。这回答了提问者想要的内容。

解决方案#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非常好
我的第三方数据库存在同样的问题 对于Alpha-Numeric字段,许多字段都是在没有前导空格的情况下输入的,dang human! 如果没有清理丢失的前导零,这就使得连接变得不可能。

结论:

在进行连接时,您可能需要考虑使用前导零填充修剪值,而不是删除前导零。 更好的是,通过添加前导零,然后重建索引来清理表中的数据 我认为这将更快,更简单。

SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF(' 0A10  ', ''))), 10)--0000000A10
SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF('', ''))), 10)--NULL --When Blank.

答案 3 :(得分:5)

而不是空格用一个通常不在列文本中的“稀有”空白字符替换0。对于像这样的列,换行可能已经足够了。然后你可以正常LTrim并再次用0替换特殊字符。

答案 4 :(得分:3)

如果字符串完全由零组成,则以下内容将返回“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

答案 5 :(得分:2)

这是一个很好的功能......

DROP FUNCTION [dbo].[FN_StripLeading]
GO
CREATE FUNCTION [dbo].[FN_StripLeading] (@string VarChar(128), @stripChar VarChar(1))
RETURNS VarChar(128)
AS
BEGIN
-- http://stackoverflow.com/questions/662383/better-techniques-for-trimming-leading-zeros-in-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

答案 6 :(得分:2)

如果string是数字

cast(value as int)将始终有效

答案 7 :(得分:1)

我的这个版本是对Arvo工作的改编,还增加了一些以确保其他两个案例。

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

答案 8 :(得分:1)

  SUBSTRING(str_col, IIF(LEN(str_col) > 0, PATINDEX('%[^0]%', LEFT(str_col, LEN(str_col) - 1) + '.'), 0), LEN(str_col))

即使使用'0','00'等也可以正常工作。

答案 9 :(得分:1)

如果您使用的是Snowflake SQL,则可以使用以下方法:

ltrim(str_col,'0')

ltrim函数从左侧删除指定字符集的所有实例。

所以'00000008A'上的ltrim(str_col,'0')将返回'8A'

在'$ 125.00'上的rtrim(str_col,'0。')将返回'$ 125'

答案 10 :(得分:1)

replace(ltrim(replace(Fieldname.TableName, '0', '')), '', '0')

Thomas G的建议符合我们的需求。

我们案例中的字段已经是字符串,只需要修剪前导零。它大多数都是数字,但有时会有字母,所以以前的INT转换会崩溃。

答案 11 :(得分:0)

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

这对可以转换为INT

的字符串的长度有限制

答案 12 :(得分:0)

如果您不想转换为int,我更喜欢下面的逻辑,因为它可以处理null     IFNULL(field,LTRIM(field,'0'))

答案 13 :(得分:0)

要将数字从 varchar 转换为 int ,您还可以使用简单

(column + 0)

答案 14 :(得分:0)

这可能有帮助

SELECT ABS(column_name) FROM [db].[schema].[table]

答案 15 :(得分:-1)

试试这个:

MERCHANTAPIURL

答案 16 :(得分:-1)

在MySQL中,您可以执行此操作...

Trim(Leading '0' from your_column)