【问题标题】:Select query to remove non-numeric characters选择查询以删除非数字字符
【发布时间】:2013-09-08 15:38:23
【问题描述】:

我在具有可变 alpha 长度的列中有脏数据。我只想去掉任何不是 0-9 的东西。

我不想运行函数或过程。我有一个类似的脚本,它只是在文本之后抓取数值,它看起来像这样:

Update TableName
set ColumntoUpdate=cast(replace(Columnofdirtydata,'Alpha #','') as int)
where Columnofdirtydata like 'Alpha #%'
And ColumntoUpdate is Null

我认为它会很好地工作,直到我发现一些我认为只是格式为 Alpha #12345789 的数据字段不是。

需要剥离的数据示例

AB ABCDE # 123
ABCDE# 123
AB: ABC# 123

我只想要 123。确实,所有数据字段在数字之前都有 #。

我尝试了 substring 和 PatIndex,但我的语法并没有完全正确。有人对解决此问题的最佳方法有任何建议吗?

【问题讨论】:

标签: tsql sql-server-2008-r2 substring patindex


【解决方案1】:

请参阅blog post,了解如何从 SQL Server 中的字符串中提取数字。以下是在您的示例中使用字符串的示例:

DECLARE @textval NVARCHAR(30)
SET @textval = 'AB ABCDE # 123'

SELECT LEFT(SUBSTRING(@textval, PATINDEX('%[0-9.-]%', @textval), 8000),
           PATINDEX('%[^0-9.-]%', SUBSTRING(@textval, PATINDEX('%[0-9.-]%', @textval), 8000) + 'X') -1)

【讨论】:

  • 答案中为什么使用数字 8000?
  • 8000 被使用,因为他正在使用前 8000 个字符,即 VARCHAR 字符串的最大大小。但是,由于文本被定义为 NVARCHAR,它可能是 4000。我的问题是,这真的有必要吗?
  • 两个 cmets: 1) 我投票赞成直接表达而不是过程或函数; 2) 要求删除除 0-9 之外的 所有 个字符的问题。这里的答案需要在3处修改以满足这个要求:将0-9.-替换为0-9(即删除3处.-)。
  • 如果您有混合的字符和数字值,则此解决方案不起作用。在 Oracle 中,我只是使用 TRANSLATE 函数来给我数字或 Alpha,但是使用 SQL Server TRANSLATE 直到 2017 年才可用,我的公司尚未升级所有实例以使用。
  • 这不起作用。请参阅此示例。 SET @textval = 'AB ABC+DE # 123+'
【解决方案2】:

您可以使用stuffpatindex

stuff(Col, 1, patindex('%[0-9]%', Col)-1, '')

SQL Fiddle

【讨论】:

  • 这只会删除第一次出现的非数字字符。
  • 它不会为我删除任何内容。
【解决方案3】:

这对我很有效:

CREATE FUNCTION [dbo].[StripNonNumerics]
(
  @Temp varchar(255)
)
RETURNS varchar(255)
AS
Begin

    Declare @KeepValues as varchar(50)
    Set @KeepValues = '%[^0-9]%'
    While PatIndex(@KeepValues, @Temp) > 0
        Set @Temp = Stuff(@Temp, PatIndex(@KeepValues, @Temp), 1, '')

    Return @Temp
End

然后像这样调用函数以查看经过清理的内容旁边的原始内容:

SELECT Something, dbo.StripNonNumerics(Something) FROM TableA

【讨论】:

  • 非常感谢!你是救生员。我修改了您的正则表达式以包含小数点Set @KeepValues = '%[^0-9].%',但除此之外,整个代码都可以按预期完美运行。 ;)
【解决方案4】:

如果数字之间可能存在某些字符(例如千位分隔符),您可以尝试以下操作:

declare @table table (DirtyCol varchar(100))
insert into @table values
    ('AB ABCDE # 123')
    ,('ABCDE# 123')
    ,('AB: ABC# 123')
    ,('AB#')
    ,('AB # 1 000 000')
    ,('AB # 1`234`567')
    ,('AB # (9)(876)(543)')

;with tally as (select top (100) N=row_number() over (order by @@spid) from sys.all_columns),
data as (
    select DirtyCol, Col
    from @table
        cross apply (
            select (select C + ''
            from (select N, substring(DirtyCol, N, 1) C from tally where N<=datalength(DirtyCol)) [1]
            where C between '0' and '9'
            order by N
            for xml path(''))
        ) p (Col)
    where p.Col is not NULL
)
select DirtyCol, cast(Col as int) IntCol
from data

输出是:

DirtyCol              IntCol
--------------------- -------
AB ABCDE # 123        123
ABCDE# 123            123
AB: ABC# 123          123
AB # 1 000 000        1000000
AB # 1`234`567        1234567
AB # (9)(876)(543)    9876543

如需更新,请将ColToUpdate 添加到data cte 的选择列表中:

;with num as (...),
data as (
    select ColToUpdate, /*DirtyCol, */Col
    from ...
)
update data
set ColToUpdate = cast(Col as int)

【讨论】:

  • 谢谢!这应该是公认的答案。你可以把它变成一个内联函数,它会比这里提到的其他函数执行得更快。
【解决方案5】:

如果您的服务器支持 TRANSLATE 功能,这是一个优雅的解决方案(在 sql server 上,它在 sql server 2017+ 和 sql azure 上可用)。

首先,它用@ 字符替换任何非数字字符。 然后,它会删除所有 @ 字符。 您可能需要添加您知道可能存在于 TRANSLATE 调用的第二个参数中的其他字符。

select REPLACE(TRANSLATE([Col], 'abcdefghijklmnopqrstuvwxyz+()- ,#+', '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'), '@', '')

【讨论】:

    【解决方案6】:
    CREATE FUNCTION FN_RemoveNonNumeric (@Input NVARCHAR(512))
    RETURNS NVARCHAR(512)
    AS
    BEGIN
    DECLARE @Trimmed NVARCHAR(512)
    
    SELECT @Trimmed = @Input
    
    WHILE PATINDEX('%[^0-9]%', @Trimmed) > 0
        SELECT @Trimmed = REPLACE(@Trimmed, SUBSTRING(@Trimmed, PATINDEX('%[^0-9]%', @Trimmed), 1), '')
    
    RETURN @Trimmed
    END
    
    GO
    
    SELECT dbo.FN_RemoveNonNumeric('ABCDE# 123')
    

    【讨论】:

      【解决方案7】:

      这是一个从字符串中提取所有数字的版本;即给定I'm 35 years old; I was born in 1982. The average family has 2.4 children.,这将返回35198224。即,如果您有可能已格式化为代码的数字数据(例如#123,456,789 / 123-00005),这很好,但如果您要提取特定数字(即与数字相反)则不合适/ 只是数字字符)来自文本。它也只处理数字;所以不会返回负号 (-) 或句点 .)。

      declare @table table (id bigint not null identity (1,1), data nvarchar(max)) 
      insert @table (data) 
      values ('hello 123 its 45613 then') --outputs: 12345613
      ,('1 some other string 98 example 4') --outputs: 1984
      ,('AB ABCDE # 123') --outputs: 123 
      ,('ABCDE# 123') --outputs: 123
      ,('AB: ABC# 123') --outputs: 123
      ; with NonNumerics as (
          select id
          , data original
          --the below line replaces all digits with blanks
          , replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(data,'0',''),'1',''),'2',''),'3',''),'4',''),'5',''),'6',''),'7',''),'8',''),'9','') nonNumeric
          from @table
      )
      --each iteration of the below CTE removes another non-numeric character from the original string, putting the result into the numerics column
      , Numerics as (
          select id
          , replace(original, substring(nonNumeric,1,1), '') numerics
          , replace(nonNumeric, substring(nonNumeric,1,1), '') charsToreplace
          , len(replace(nonNumeric, substring(nonNumeric,1,1), '')) charsRemaining
          from NonNumerics
      
          union all
      
          select id
          , replace(numerics, substring(charsToreplace,1,1), '') numerics
          , replace(charsToreplace, substring(charsToreplace,1,1), '') charsToreplace
          , len(replace(charsToreplace, substring(charsToreplace,1,1), '')) charsRemaining
          from Numerics
          where charsRemaining > 0
      )
      --we select only those strings with `charsRemaining=0`; i.e. the rows for which all non-numeric characters have been removed; there should be 1 row returned for every 1 row in the original data set.
      select * from Numerics where charsRemaining = 0
      

      此代码通过将给定字符串中的所有数字(即我们想要的字符)替换为空白来工作。然后它通过原始字符串(包括数字)删除所有留下的字符(即非数字字符),从而只留下数字。

      我们分两步执行此操作,而不是一开始就删除所有非数字字符的原因是只有 10 位数字,而可能的字符数量很多;所以替换那个小列表相对较快;然后给我们一个实际存在于字符串中的非数字字符的列表,这样我们就可以替换那个小集合。

      该方法利用递归 SQL,使用公用表表达式 (CTE)。

      【讨论】:

        【解决方案8】:

        要添加到Ken's 答案,它会处理逗号、空格和括号

        --Handles parentheses, commas, spaces, hyphens..
        declare @table table (c varchar(256))
        insert into @table
        values
        ('This is a test 111-222-3344'),
        ('Some Sample Text (111)-222-3344'),
        ('Hello there 111222 3344 / How are you?'),
        ('Hello there 111 222 3344 ? How are you?'),
        ('Hello there 111 222 3344. How are you?')
        
        select
        replace(LEFT(SUBSTRING(replace(replace(replace(replace(replace(c,'(',''),')',''),'-',''),' ',''),',',''), PATINDEX('%[0-9.-]%', replace(replace(replace(replace(replace(c,'(',''),')',''),'-',''),' ',''),',','')), 8000),
                   PATINDEX('%[^0-9.-]%', SUBSTRING(replace(replace(replace(replace(replace(c,'(',''),')',''),'-',''),' ',''),',',''), PATINDEX('%[0-9.-]%', replace(replace(replace(replace(replace(c,'(',''),')',''),'-',''),' ',''),',','')), 8000) + 'X') -1),'.','')
        from @table
        

        【讨论】:

          【解决方案9】:
          Create function fn_GetNumbersOnly(@pn varchar(100))
              Returns varchar(max)
              AS
              BEGIN
                Declare @r varchar(max) ='', @len int ,@c char(1), @x int = 0
                Select @len = len(@pn)
                while @x <= @len 
                begin
                  Select @c = SUBSTRING(@pn,@x,1)
                  if ISNUMERIC(@c) = 1 and @c <> '-'
                   Select @r = @r + @c
                 Select @x = @x +1
                end
              return @r
          End
          

          【讨论】:

          • 你能解释一下你的答案吗?
          【解决方案10】:

          在您的情况下,# 似乎总是在 # 符号之后,因此将 CHARINDEX() 与 LTRIM() 和 RTRIM() 一起使用可能会表现最好。但这是摆脱任何非数字的有趣方法。它利用计数表和数字表来限制接受哪些字符,然后使用 XML 技术连接回没有非数字字符的单个字符串。这种技术的巧妙之处在于它可以扩展为包含任何允许的字符并删除任何不允许的字符。

          DECLARE @ExampleData AS TABLE (Col VARCHAR(100))
          INSERT INTO @ExampleData (Col) VALUES ('AB ABCDE # 123'),('ABCDE# 123'),('AB: ABC# 123')
          
          DECLARE @Digits AS TABLE (D CHAR(1))
          INSERT INTO @Digits (D) VALUES ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9')
          
          ;WITH cteTally AS (
          SELECT
              I = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
          FROM
              @Digits d10
              CROSS APPLY @Digits d100
              --add more cross applies to cover longer fields this handles 100
          )
          
          SELECT *
          FROM
              @ExampleData e
              OUTER APPLY (
              SELECT CleansedPhone = CAST((
              SELECT TOP 100
                 SUBSTRING(e.Col,t.I,1)
              FROM
                 cteTally t
                 INNER JOIN @Digits d
                 ON SUBSTRING(e.Col,t.I,1) = d.D
              WHERE
                 I <= LEN(e.Col)
              ORDER BY
                 t.I
              FOR XML PATH('')) AS VARCHAR(100))) o
          

          【讨论】:

            【解决方案11】:

            聚会很晚了,我发现了以下我虽然工作出色的东西..如果有人还在看

            SELECT
                (SELECT CAST(CAST((
                    SELECT SUBSTRING(FieldToStrip, Number, 1)
                    FROM master..spt_values
                    WHERE Type='p' AND Number <= LEN(FieldToStrip) AND
                        SUBSTRING(FieldToStrip, Number, 1) LIKE '[0-9]' FOR XML Path(''))
                AS xml) AS varchar(MAX)))
            FROM
                SourceTable
            

            【讨论】:

            • 帮我剥离电话号码非数字字符
            【解决方案12】:
             Declare @MainTable table(id int identity(1,1),TextField varchar(100))
              INSERT INTO @MainTable (TextField)
             VALUES
             ('6B32E')
             declare @i int=1
              Declare @originalWord varchar(100)=''
              WHile @i<=(Select count(*) from @MainTable)
              BEGIN
              Select @originalWord=TextField from @MainTable where id=@i
            
             Declare @r varchar(max) ='', @len int ,@c char(1), @x int = 0
            
                Select @len = len(@originalWord)
                declare @pn varchar(100)=@originalWord
                while @x <= @len 
                begin
            
                  Select @c = SUBSTRING(@pn,@x,1)
                if(@c!='')
                BEGIN
                        if ISNUMERIC(@c) = 0 and @c <> '-'
                BEGIN
                 Select @r = cast(@r as varchar) + cast(replace((SELECT ASCII(@c)-64),'-','') as varchar)
            
               end
               ELSE
               BEGIN
                Select @r = @r + @c
            
            
               END
            
            END
            
            
                Select @x = @x +1
            
                END
                Select @r
              Set @i=@i+1
              END
            

            【讨论】:

              【解决方案13】:

              我为此创建了一个函数

              Create FUNCTION RemoveCharacters (@text varchar(30))
              RETURNS VARCHAR(30)
              AS
              BEGIN
              declare @index as int 
              declare @newtexval as varchar(30)
              set @index = (select PATINDEX('%[A-Z.-/?]%', @text))
              if (@index =0)
              begin 
              return @text
              end
              else
              begin 
              set @newtexval  = (select STUFF ( @text , @index , 1 , '' ))
              return dbo.RemoveCharacters(@newtexval)
              end
              return 0
              END
              GO
              

              【讨论】:

                【解决方案14】:

                答案如下:

                DECLARE @t TABLE (tVal VARCHAR(100))
                
                INSERT INTO @t VALUES('123')
                INSERT INTO @t VALUES('123S')
                INSERT INTO @t VALUES('A123,123')
                INSERT INTO @t VALUES('a123..A123')
                
                
                ;WITH cte (original, tVal, n)
                     AS
                     (
                         SELECT t.tVal AS original,
                                LOWER(t.tVal)  AS tVal,
                                65             AS n
                         FROM   @t             AS t
                         UNION ALL
                         SELECT tVal AS original,
                                CAST(REPLACE(LOWER(tVal), LOWER(CHAR(n)), '') AS VARCHAR(100)),
                                n + 1
                         FROM   cte
                         WHERE  n <= 90
                     )
                
                SELECT t1.tVal  AS OldVal,
                       t.tval   AS NewVal
                FROM   (
                           SELECT original,
                                  tVal,
                                  ROW_NUMBER() OVER(PARTITION BY tVal + original ORDER BY original) AS Sl
                           FROM   cte
                           WHERE  PATINDEX('%[a-z]%', tVal) = 0
                       ) t
                       INNER JOIN @t t1
                            ON  t.original = t1.tVal
                WHERE  t.sl = 1
                

                【讨论】:

                  【解决方案15】:

                  您可以创建 SQL CLR 标量函数,以便能够使用正则表达式,如替换模式。

                  Here您可以找到如何创建此类功能的示例。

                  拥有这样的功能只需以下几行即可解决问题:

                  SELECT [dbo].[fn_Utils_RegexReplace] ('AB ABCDE # 123', '[^0-9]', '');
                  SELECT [dbo].[fn_Utils_RegexReplace] ('ABCDE# 123', '[^0-9]', '');
                  SELECT [dbo].[fn_Utils_RegexReplace] ('AB: ABC# 123', '[^0-9]', '');
                  

                  更重要的是,您将能够解决更复杂的问题,因为正则表达式将直接在您的 T-SQL 语句中带来全新的选项世界。

                  【讨论】:

                    【解决方案16】:

                    声明@STR VARCHAR(400)

                    DECLARE @specialchars VARCHAR(50) = '%[~,@,#,$,%,&,*,(,),!^?:]%'

                    SET @STR = '1, 45 4,3 68.00-'

                    WHILE PATINDEX(@specialchars, @STR) > 0

                    ---使用替换功能删除特殊字符

                    SET @STR = Replace(Replace(REPLACE(@STR, SUBSTRING(@STR, PATINDEX(@specialchars, @STR), 1),''),'-',''), '','' )

                    选择@STR

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2012-10-13
                      • 1970-01-01
                      • 2014-10-05
                      • 2017-10-16
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-03-27
                      相关资源
                      最近更新 更多