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

问题描述 投票:0回答:20

我已经使用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 有很多性能问题。

sql sql-server sql-server-2005 t-sql string
20个回答
383
投票
SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col))

62
投票

为什么不将值转换为

INTEGER
,然后返回到
VARCHAR

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

--------
       0

18
投票

如果您有全零(甚至单个零),则无需考虑此处的其他答案。
有些总是将空字符串默认为零,当它应该保持空白时这是错误的。
重新阅读原来的问题。 这回答了发问者想要的。

解决方案#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 性能问题的担忧是可以理解的。
然而,这个问题仅适用于全标量函数和多语句表函数。
使用 ITVF 非常好。

我的第 3 方数据库也有同样的问题。
对于字母数字字段,许多输入时没有前导空格,该死的人类!
这使得在不清理丢失的前导零的情况下无法进行连接。

结论:

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

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

7
投票

将 0 替换为通常不应出现在列文本中的“罕见”空白字符,而不是空格。 对于这样的列来说,换行可能就足够了。 然后就可以正常LTrim并将特殊字符再次替换为0。


6
投票

我的版本是对 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

6
投票
SELECT CAST(CAST('000000000' AS INTEGER) AS VARCHAR)

这对可转换为 INT 的字符串长度有限制


4
投票

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

4
投票

这是一个很好的函数......

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

4
投票
如果字符串是数字,则

cast(value as int) 将始终有效


3
投票

如果您使用的是 Snowflake SQL,可以使用这个:

ltrim(str_col,'0')

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

因此 '00000008A' 上的 ltrim(str_col,'0') 将返回 '8A'

“$125.00”上的 rtrim(str_col,'0.') 将返回“$125”


3
投票

这可能有帮助

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

2
投票

试试这个:

replace(ltrim(replace(@str, '0', ' ')), ' ', '0')

2
投票
replace(ltrim(replace(Fieldname.TableName, '0', '')), '', '0')

Thomas G 的建议满足了我们的需求。

我们例子中的字段已经是字符串,只需要修剪前导零。大多数情况下都是数字,但有时也有字母,因此之前的 INT 转换会崩溃。


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

即使使用“0”、“00”等也能正常工作。


1
投票

要将数字转换为 varcharint,您还可以使用 simple

(column + 0)

1
投票

非常简单的方法,当您只使用数值时:

SELECT
    TRY_CONVERT(INT, '000053830')

0
投票

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


0
投票

从 SQL Server 2022 (16.x) 开始,您可以执行此操作

TRIM ( [ LEADING | TRAILING | BOTH ] [characters FROM ] string )


0
投票

如何使用

TRIM()
删除空格和 0。

SELECT NAME, TRIM(NAME,' 0') as N, CONCAT('<', N,'>')
FROM CUSTOMERS;

enter image description here


-3
投票

在 MySQL 中你可以这样做...

Trim(Leading '0' from your_column)
© www.soinside.com 2019 - 2024. All rights reserved.