【问题标题】:Convert comma delimited ID list to Comma delimited prefix List SQL Server将逗号分隔的 ID 列表转换为逗号分隔的前缀列表 SQL Server
【发布时间】:2016-10-24 14:59:19
【问题描述】:

我正在尝试将逗号分隔的 ID 列表转换为逗号分隔的前缀列表。

BKR394859607,MTP293840284,SPN489620586

我的目标是将我在 SQL Server 中的 IDString 中的这 3 个 Id 转换为

BKR,MTP,SPN

这个字符串可以有无限个Ids

Declare @ActivityID as varchar(MAX) = 'BKR394859607,MTP293840284,SPN489620586'
Declare @ActivityPrefixes as varchar(MAX)
Set @ActivityPrefixes = (function to Convert to comma delimited prefix list)

【问题讨论】:

  • 总是前三个字母吗
  • 您可以使用临时表、表变量或表值参数来代替这个分隔列表吗?要将其作为字符串处理,您必须首先将其拆分为一个表格,然后删除除前三个字符之外的所有字符,最后将其全部重新推回一个分隔列表。
  • 是的,前缀始终是前三个字母。我也在使用 SQL Server 2014。

标签: sql-server function delimiter


【解决方案1】:

正如 Sean Lange 在评论中提到的,您需要执行三个步骤

  1. 将字符串拆分成单独的行
  2. 去掉所需的字符
  3. 将行连接回 CSV。

像这样的

DECLARE @ActivityID AS VARCHAR(max) = 
        'BKR394859607,MTP293840284,SPN489620586,GHY489620586' 

SELECT Stuff(split_val, 1, 1, '') as Result
FROM   (SELECT ',' 
               + LEFT(split.a.value('.', 'VARCHAR(100)'), 3) 
        FROM   (SELECT Cast ('<M>' + Replace(@ActivityID, ',', '</M><M>') 
                             + '</M>' AS XML) AS Data) AS A 
               CROSS apply data.nodes ('/M') AS Split(a) 
        FOR xml path(''))a(split_val) 

结果: BKR,MTP,SPN,GHY

【讨论】:

    【解决方案2】:

    试试这个:

    DECLARE @ActivityID AS VARCHAR(MAX) = 'BKR394859607,MTP293840284,SPN489620586';
    
    DECLARE @ActivityPrefixes AS VARCHAR(MAX);
    
    SET @ActivityPrefixes =  REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        REPLACE(REPLACE(REPLACE(REPLACE(
             @ActivityID,'0', ''), '1',''), '2', ''),'3', ''), '4',''), '5', ''),
                         '6', ''),'7', ''), '8', ''), '9', '');
    SELECT @ActivityPrefixes
    

    如果您想获取每个部分的前 3 个字符,请尝试以下操作:

    DECLARE @ActivityID AS VARCHAR(MAX) = 'BKR39A859607,M3TP293840284,SP1N4896GG586,BKR394859607,MTP293840284,SPN489620586';
    
    DECLARE @ActivityPrefixes AS VARCHAR(MAX);
    SET @ActivityPrefixes = '';
    
    WHILE 1 = 1
    BEGIN
    
        DECLARE @i INT;
    
        IF CHARINDEX(',', @ActivityID) = 0
            SET @i = 3;
        ELSE
            SET @i = CHARINDEX(',', @ActivityID) - 1;
    
        IF @ActivityPrefixes = ''
            SET @ActivityPrefixes = SUBSTRING(SUBSTRING(@ActivityID, 1, @i), 1,3);
        ELSE
            SET @ActivityPrefixes = @ActivityPrefixes + ','
                + SUBSTRING(SUBSTRING(@ActivityID, 1, @i), 1, 3);
    
        IF CHARINDEX(',', @ActivityID) = 0
            BREAK;
    
        SET @ActivityID = SUBSTRING(@ActivityID,CHARINDEX(',', @ActivityID) + 1,LEN(@ActivityID));
    END;
    
    SELECT  @ActivityPrefixes;
    

    【讨论】:

    • 如果前 3 个字符包含数字或任何其他字符不是数字,这将不起作用。
    • @SeanLange 请提供此方法不起作用的数据示例
    • 与我提供的测试数据配合得很好,是最简单的。然而,真实数据偶尔会包含字母..
    • 好的,试试这个值列表。 "BKR39A859607,M3TP293840284,SP1N4896GG586"
    • 呃,但是现在您进入了一个 while 循环,以获取应该基于设置的内容。
    【解决方案3】:

    首先,阅读以下文章,他们讨论了拆分字符串(以及不这样做的原因),并比较了在无法避免的情况下进行拆分的方法的性能。

    这三篇文章的结果是(以防链接失效):

    1. 尽可能避免将分隔列表作为字符串,如果您需要存储列表,表格是更好的选择。
    2. 如果必须这样做,CLR 是最可扩展(且最准确的方法)。
    3. 如果您可以确定没有要拆分的特殊 XML 字符,则将分隔的字符串转换为 XML,然后使用 XQuery 来获取各个项目的效果很好。
    4. 否则,最好使用交叉连接构建计数表。

    最通用的方法是最后一种,因为不是每个人都可以使用CLR,并且保证没有特殊的XML字符,所以拆分方法是:

    CREATE FUNCTION [dbo].[Split]
    (
       @List       NVARCHAR(MAX),
       @Delimiter  NVARCHAR(255)
    )
    RETURNS TABLE
    WITH SCHEMABINDING AS
    RETURN
    (   WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1), (1)) n (N)),
        N2(N) AS (SELECT 1 FROM N1 a CROSS JOIN N1 b),
        N3(N) AS (SELECT 1 FROM N2 a CROSS JOIN N2 b),
        N4(N) AS (SELECT 1 FROM N3 a CROSS JOIN N3 b),
        cteTally(N) AS 
        (   SELECT 0 UNION ALL 
            SELECT TOP (DATALENGTH(ISNULL(@List,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
            FROM n4
        ),
        cteStart(N1) AS 
        (   SELECT t.N+1 
            FROM cteTally t
            WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0)
        )
        SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000)), 
                Position = s.N1,
                ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1)
        FROM cteStart s
    );
    

    现在您有了拆分函​​数,您可以拆分第一个字符串:

    DECLARE @ActivityID AS VARCHAR(MAX) = 'BKR394859607,MTP293840284,SPN489620586';
    
    SELECT  Item, 
            NewID = LEFT(Item, 3)
    FROM    dbo.Split(@ActivityID, ',');
    

    这给了你:

    Item            NewID
    -----------------------------
    BKR394859607    BKR
    MTP293840284    MTP
    SPN489620586    SPN
    

    然后您可以使用FOR XML PATH() 连接此备份:

    DECLARE @ActivityID AS VARCHAR(MAX) = 'BKR394859607,MTP293840284,SPN489620586';
    
    SELECT  STUFF(( SELECT  ',' + LEFT(Item, 3)
                    FROM    dbo.Split(@ActivityID, ',')
                    FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '');
    

    有关其工作原理的更多信息,请参阅this answer

    最佳解决方案可能是使用用户定义的表类型来存储字符串列表:

    CREATE TYPE dbo.StringList AS TABLE (Value VARCHAR(MAX));
    

    那么,与其建立一个分隔字符串,不如建立一个表:

    DECLARE @Activity dbo.StringList;
    INSERT @Activity (Value)
    VALUES ('BKR394859607'), ('MTP293840284'), ('SPN489620586');
    

    这样你就可以避免痛苦的分裂,并且可以更轻松地操纵每个单独的记录。

    如果你确实需要得到一个新的分隔字符串,那么你可以使用与上面相同的逻辑:

    DECLARE @Activity dbo.StringList;
    INSERT @Activity (Value)
    VALUES ('BKR394859607'), ('MTP293840284'), ('SPN489620586');
    
    SELECT  STUFF(( SELECT  ',' + LEFT(Value, 3)
                    FROM    @Activity
                    FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '');
    

    【讨论】:

      【解决方案4】:

      使用下面的answer实现T-SQL中的regeular表达式函数。然后您可以使用以下内容:

      【讨论】:

        猜你喜欢
        • 2021-05-18
        • 1970-01-01
        • 2021-10-21
        • 1970-01-01
        • 2017-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多