【问题标题】:Replace with wildcard, in SQL在 SQL 中用通配符替换
【发布时间】:2013-01-09 19:08:29
【问题描述】:

我知道 MS T-SQL 不支持正则表达式,但我需要类似的功能。这是我正在尝试做的事情:

我有一个存储面包屑的 varchar 表字段,如下所示:

/ID1:Category1/ID2:Category2/ID3:Category3/

每个类别名称前面都有其类别 ID,以冒号分隔。我想选择并显示这些面包屑,但我想删除类别 ID 和冒号,如下所示:

/Category1/Category2/Category3/

前导斜杠 (/) 直到并包括冒号 (:) 之间的所有内容都应删除。

我没有提取数据、在外部对其进行操作以及重新插入表中的选项;所以我试图在 SELECT 语句中完成此操作。

由于 SELECT 中返回的行数,我也无法使用游标循环遍历每一行并使用嵌套循环清理每个字段。

这个可以吗?

谢谢大家 - 杰

【问题讨论】:

  • 什么数据库产品和版本?
  • 使用 CTE 的简单选择语句回答,如下

标签: sql sql-server tsql string


【解决方案1】:

我认为您最好的选择是使用递归用户定义函数 (UDF)。我在这里包含了一些代码,您可以使用这些代码传入字符串以实现您正在寻找的结果。

CREATE FUNCTION ufn_StripIDsFromBreadcrumb (@cIndex int, @breadcrumb varchar(max), @theString varchar(max))

RETURNS varchar(max)

AS

BEGIN
DECLARE @nextColon int
DECLARE @nextSlash int

SET @nextColon = CHARINDEX(':', @theString, @cIndex)
SET @nextSlash = CHARINDEX('/', @theString, @nextColon)
SET @breadcrumb = @breadcrumb + SUBSTRING(@theString, @nextColon + 1, @nextSlash - @nextColon)

IF @nextSlash != LEN(@theString)

     BEGIN
     exec @breadcrumb = ufn_StripIDsFromBreadcrumb @cIndex =  @nextSlash, @breadcrumb = @breadcrumb, @theString = @theString
     END
RETURN @breadcrumb
END

然后你可以执行它:

DECLARE @myString varchar(max)
EXEC @myString = ufn_StripIDsFromBreadcrumb 1, '/', '/ID1:Category1/ID2:Category2/ID3:Category3/'
PRINT @myString

【讨论】:

  • 这是我们当前解决方案中最容易实施的方法。正如 Josh 和 OMG Ponies 所建议的那样,我看到了启用 CLR 的引用,但我没有想到递归。非常感谢所有输入。
【解决方案2】:

这适用于 SQL Server 2005 及更高版本。

create table strings (
  string varchar(1000)
)

insert into strings values( '/ID1:Category1/ID2:Category2/ID3:Category3/' )  
insert into strings values( '/ID4:Category4/ID5:Category5/ID8:Category6/' )  
insert into strings values( '/ID7:Category7/ID8:Category8/ID9:Category9/' )  
go

with  
replace_with_wildcard ( restrung ) as 
( 
  select replace( string, '', '' ) 
  from strings

  union all 

  select  
    replace( restrung, substring( restrung, patindex( '%ID%', restrung ), 4 ), '' ) 
  from replace_with_wildcard 
  where patindex( '%ID%', restrung ) > 0 
) 

select restrung
from replace_with_wildcard 
where charindex( ':', restrung ) = 0
order by restrung

drop table strings 

【讨论】:

    【解决方案3】:

    您也许可以使用拆分功能来做到这一点。以下拆分函数依赖于 Numbers 表的存在,该表实际上包含一个顺序的数字列表,如下所示:

    Create Table dbo.Numbers( Value int not null primary key clustered )
    GO
    With Nums As
        (
        Select ROW_NUMBER() OVER( Order By o.object_id ) As Num
        From sys.objects as o
            cross join sys.objects as o2
        )
    Insert dbo.Numbers( Value )
    Select Num
    From Nums
    Where Num Between 1 And 10000
    GO  
    
    
    Create Function [dbo].[udf_Split] (@DelimitedList nvarchar(max), @Delimiter nvarchar(2) = ',')
    Returns @SplitResults TABLE (Position int NOT NULL PRIMARY KEY, Value nvarchar(max))
    AS
    /*
    PURPOSE: to split the @DelimitedList based on the @Delimter
    DESIGN NOTES:
        1. In general the contents of the next item is: NextDelimiterPosition - CurrentStartPosition
        2. CurrentStartPosition = 
            CharIndex(@Delimiter, A.list, N.Value)  = Current Delimiter position
            + Len(@Delimiter)                       + The number of delimiter characters 
            + 1                                     + 1 since the text of the item starts after the delimiter
        3. We need to calculate the delimiter length because the LEN function excludes trailing spaces. Thus
            if a delimiter of ", " (a comma followed by a space) is used, the LEN function will return 1.
        4. The DataLength function returns the number of bytes in the string. However, since we're using
            an nvarchar for the delimiter, the number of bytes will double the number of characters.
    */
    Begin
        Declare @DelimiterLength int
        Set @DelimiterLength = DataLength(@Delimiter) / 2
    
        If Left(@DelimitedList, @DelimiterLength) <> @Delimiter
            Set @DelimitedList = @Delimiter + @DelimitedList
    
        If Right(@DelimitedList, @DelimiterLength) <> @Delimiter
            Set @DelimitedList = @DelimitedList + @Delimiter
    
        Insert @SplitResults(Position, Value)
        Select CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength            
            , Substring (
                        A.List
                        , CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength         
                        , CharIndex(@Delimiter, A.list, N.Value + 1)                            
                            - ( CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength ) 
                        )
        From dbo.Numbers As N
            Cross Join (Select @DelimitedList As list) As A
        Where N.Value > 0
            And N.Value < LEN(A.list)
            And Substring(A.list, N.Value, @DelimiterLength) = @Delimiter
        Order By N.Value
    
        Return
    End
    

    然后,您也许可以像这样在去掉前缀的地方运行查询:

    Select Table, Substring(S.Value, CharIndex(':', S.Value) + 1, Len(S.Value))
    From Table
        Cross Apply dbo.udf_Split(Table.ListColumn, '/') As S
    

    这将为您提供如下值:

    Category1
    Category2
    Category3
    

    然后您可以使用FOR XML PATH 再次组合它们:

    Select Table.PK
        ,   Stuff(  (
                    Select '/' + Substring(S.Value, CharIndex(':', S.Value) + 1, Len(S.Value))
                    From Table As Table1
                        Cross Apply dbo.udf_Split(Table.ListColumn, '/') As S1
                    Where Table1.PK = Table.PK
                    Order By S1.Position
                    For Xml Path('')
                    ), 1, 1, '') As BreadCrumb
    From Table
    

    【讨论】:

    • 我的建议是您创建一个新字段来存储您想要的值并使用能够填充它的过程,然后填充数据输入的所有新记录(以及对原始记录的任何更改)字段)何时需要更少的时间。每次需要选择时都尝试执行这种过程是很痛苦的。
    • @HLGEM - 同意。我将使用上述功能来一次性填充“已清理”的面包屑列。
    • 我也同意,每次运行 SELECT 都会有点麻烦。但是,此时创建、填充和维护一个额外的“已清理”列并不是我的选择。我需要另一种方法。非常感谢您的贡献,尽管如此,Thomas。
    【解决方案4】:

    对于 SQL Server 2005+,您可以通过以下方式获得正则表达式支持:

    1. 启用 CLR(不需要重启实例)
    2. 上传您的 CLR 功能(在本例中为正则表达式替换)

    使用本机 TSQL,您需要为要删除的所有内容定义 REPLACE 语句:

    SELECT REPLACE(
             REPLACE(
               REPLACE(''/ID1:Category1/ID2:Category2/ID3:Category3/'', 'ID1:', ''),
                'ID2:', ''), 
             'ID3:', '')
    

    正则表达式或其他方式,您需要确保这些模式不会出现在实际数据中。

    【讨论】:

      【解决方案5】:

      您可以使用 SQL CLR。这是MSDN article

      【讨论】:

        【解决方案6】:
        declare @test1 nvarchar(max)
        set @test1='/ID1:Category1/ID2:Category2/ID3:Category3/'
        while(CHARINDEX('ID',@test1)<>0)
        Begin
        select @test1=REPLACE(@test1,SUBSTRING(@test1,CHARINDEX('ID',@test1),CHARINDEX(':',@test1)-
        CHARINDEX('ID',@test1)+1),'') 
        End
        select @test1
        

        【讨论】:

          猜你喜欢
          • 2016-11-24
          • 2012-02-01
          • 2013-04-01
          • 1970-01-01
          • 2015-08-03
          • 2019-08-12
          • 2017-02-23
          • 2014-01-26
          • 1970-01-01
          相关资源
          最近更新 更多