如何替换在sql中相同位置的其他列值的特殊字符后?

时间:2017-10-09 09:50:58

标签: sql sql-server sql-server-2008-r2

如何用sql中的其他列相同位置特殊字符串替换特殊字符之间的缺失字符串?以下是示例

例如:

ColumnA                columnB        Output of columnb 
~as~df~gf~er            ~qw~~~          ~qw~df~gf~er
~Evening~Afternoon       ~~             ~Evening~Afternoon
~qw~dg~er~rt~yu~io~ty   ~df~~rt~~we~~   ~df~dg~rt~rt~we~io~ty

注意:两列特殊字符数相同。 我需要像这样的列输出~qw~df~gf~er。 我希望在特殊字符的所有缺失字符串中动态更改。请帮助我

2 个答案:

答案 0 :(得分:0)

您需要正确的功能,这很容易解决。例如,在我的数据库中,我有:

DECLARE @DataSource TABLE
(   
    [ColumnA] VARCHAR(1024)
   ,[ColumnB] VARCHAR(1024)
);

INSERT INTO @DataSource ([ColumnA], [ColumnB])
VALUES ('~as~df~gf~er', '~qw~~~')
      ,('~Evening~Afternoon', '~~')
      ,('~qw~dg~er~rt~yu~io~ty', '~df~~rt~~we~~');


SELECT DS.[ColumnA]
      ,DS.[ColumnB]
      ,[dbo].[ConcatenateWithOrderAndDelimiter] (A.[index], ISNULL(NULLIF(B.[value], ''), A.[value]), '~')
FROM @DataSource DS
CROSS APPLY [dbo].[fn_Utils_RegexSplitWithOrder] ([ColumnA], '~') A
LEFT JOIN
(
    SELECT [ColumnA]
          ,B.[index]
          ,B.[value]
    FROM @DataSource DS
    CROSS APPLY [dbo].[fn_Utils_RegexSplitWithOrder] ([ColumnB], '~') B
) B
    ON DS.[ColumnA] = B.[ColumnA]
    AND A.[index] = B.[index]
GROUP BY DS.[ColumnA]
        ,DS.[ColumnB];

enter image description here

所以,基本上你需要两个功能:

  1. 一个按值拆分字符串
  2. 一个用于字符串聚合(连接)
  3. 在我的情况下,我正在使用SQL CLR功能 - 您可以找到更多关于它们的here

    在SQL Server 2017中,我们可以使用STRING_AGG,在SQL Server 2016中,我们可以使用STRING_SPIT

    如果您不想浪费时间来实现SQL CLR功能,可以添加一个拆分功能(网络中有很多功能)。例如,我使用过这个:

    CREATE FUNCTION [dbo].[fn_Analysis_ConvertCsvListToNVarCharTableWithOrder](@List nvarchar(max), @Delimiter nvarchar(10) = ',')
    RETURNS @result TABLE 
    (   
        [Value] nvarchar(max),
        [SortOrder] bigint NOT NULL
    )
    AS
    BEGIN
        IF @Delimiter is null 
        BEGIN
            SET @Delimiter = ','
        END
        DECLARE @XML xml = N'<r><![CDATA[' + REPLACE(@List, @Delimiter, ']]></r><r><![CDATA[') + ']]></r>'
    
        DECLARE @BufTable TABLE (Value nvarchar(max), SortOrder bigint NOT NULL IDENTITY(1, 1) PRIMARY KEY)
        INSERT INTO @BufTable (Value)
            SELECT Tbl.Col.value('.', 'nvarchar(max)')
            FROM   @xml.nodes('//r') Tbl(Col)
            OPTION (OPTIMIZE FOR (@xml = NULL)) 
    
        INSERT INTO @result (Value, SortOrder)
            SELECT Value, SortOrder
            FROM @BufTable
    
        RETURN
    END
    

    这应该会让你想要你想要的东西:

    WITH DataSource AS
    (
        SELECT DS.[ColumnA]
              ,DS.[ColumnB]
              ,A.[SortOrder]
              ,ISNULL(NULLIF(B.[value], ''), A.[Value]) AS [Value]
        FROM @DataSource DS
        CROSS APPLY [dbo].[fn_Analysis_ConvertCsvListToNVarCharTableWithOrder] (REPLACE([ColumnA], ',', '~'), '~') A
        LEFT JOIN
        (
            SELECT [ColumnA]
                  ,B.[SortOrder]
                  ,B.[value]
            FROM @DataSource DS
            CROSS APPLY [dbo].[fn_Analysis_ConvertCsvListToNVarCharTableWithOrder] (REPLACE([ColumnB], ',', '~'), '~') B
        ) B
            ON DS.[ColumnA] = B.[ColumnA]
            AND A.[SortOrder] = B.[SortOrder]
    )
    SELECT DISTINCT A.[ColumnA]
                   ,A.[ColumnB]
                   ,DS.[value]
    FROM DataSource A
    CROSS APPLY
    (
        SELECT STUFF
        (
            (
                SELECT '~' + B.[Value]
                FROM DataSource B
                WHERE A.[ColumnA]  = B.[ColumnA]
                ORDER BY B.[SortOrder]
                FOR XML PATH(''), TYPE
            ).value('.', 'VARCHAR(MAX)')
            ,1
            ,1
            ,''
        )
    ) DS ([value]);
    

答案 1 :(得分:0)

您可以尝试不创建Function(): -

DECLARE @ColumnA NVARCHAR(MAX);
DECLARE @columnB NVARCHAR(MAX);

SET @ColumnA = '~as~df~gf~er ';
SET @columnB = ' ~qw~~~';

;WITH CTE
     AS (
     SELECT Split.a.value('.', 'NVARCHAR(MAX)') DATA,
            ROW_NUMBER() OVER(ORDER BY @ColumnA) RN
     FROM
     (
         SELECT CAST('<M>'+REPLACE(@ColumnA, '~', '</M><M>')+'</M>' AS XML) AS String
     ) AS A
     CROSS APPLY String.nodes('/M') AS Split(a)),
     CTE1
     AS (
     SELECT Split.a.value('.', 'NVARCHAR(MAX)') DATA1,
            ROW_NUMBER() OVER(ORDER BY @columnB) RN
     FROM
     (
         SELECT CAST('<M>'+REPLACE(@columnB, '~', '</M><M>')+'</M>' AS XML) AS String
     ) AS A
     CROSS APPLY String.nodes('/M') AS Split(a))
     SELECT @ColumnA AS ColumnA,
            @columnB AS columnB,
            [Output of columnb] =
     (
         SELECT '~'+CASE
                        WHEN C1.DATA1 = ''
                        THEN C2.DATA
                        ELSE C1.DATA1
                    END
         FROM CTE1 C1
              INNER JOIN CTE C2 ON C2.RN = C1.RN
                                   AND C1.RN > 1 FOR XML PATH('')
     );

期望输出:

ColumnA                columnB        Output of columnb 
~as~df~gf~er            ~qw~~~          ~qw~df~gf~er