SQL 服务器:按位置插入字符串

SQL Server: String insertion by position

我想在给定字符串的指定位置插入一组值。我找不到现有的解决方案,所以我创建了以下查询。我想知道是否有一种方法可以简化它,尤其是查询 cte 和创建 NewString 的部分。另外,有没有办法在不涉及 ID 列的情况下构造 cte 递归? ID(和其他)似乎工作正常,只是想知道是否可以对其进行调整以使其更多 organic/elegant 或者是否有更好的整体解决方案。不同的组实际上仅用于测试目的;每个组对应一个可能的插入位置场景。这样我用的时候就不会涉及群了。

declare
    @String varchar(200),
    @StringLen int,
    @GroupID int,
    @PositionMax int

declare @Chars table (
    ID int identity,
    GroupID int,
    Position int,
    Value varchar(20)
    )

select
    @String = 'abcde',
    @StringLen = len(@String),
    @GroupID = 1

--Affix
--[P]refix
--[I]nfix
--[S]uffix

insert @Chars
select
    GroupID,
    Position,
    Value
from (
    values
        (1, 0, 'X'),    --P
        (2, 2, 'Y'),    --I
        (3, 5, 'Z'),    --S
        (4, 0, 'X'),    --P
        (4, 2, 'Y'),    --I
        (5, 2, 'Y'),    --I
        (5, 5, 'Z'),    --S
        (6, 0, 'X'),    --P
        (6, 5, 'Z'),    --S
        (7, 0, 'X'),    --P
        (7, 2, 'Y'),    --I
        (7, 5, 'Z'),    --S
        (8, 2, 'Y1'),   --I
        (8, 4, 'Y2'),   --I
        (9, 0, 'X'),    --P
        (9, 2, 'Y1'),   --I
        (9, 4, 'Y2'),   --I
        (10, 2, 'Y1'),  --I
        (10, 4, 'Y2'),  --I
        (10, 5, 'Z'),   --S
        (11, 0, 'X'),   --P
        (11, 2, 'Y1'),  --I
        (11, 4, 'Y2'),  --I
        (11, 5, 'Z')    --S
    ) as T(GroupID, Position, Value)
order by GroupID, Position

;with cte (
    ID,
    GroupID,
    LeftString,
    Value,
    RightString,
    Position
    ) as (
    select
        ID,
        GroupID,
        LeftString,
        Value,
        RightString,
        Position
    from (
        select
            row_number() over (partition by GroupID order by ID) as RowNumber,
            ID,
            GroupID,
            cast(left(@String, Position) as varchar(200)) as LeftString,
            Value,
            cast(right(@String, @StringLen - Position) as varchar(200)) as RightString,
            Position
        from @Chars
        ) as C
    where RowNumber = 1
    union all
    select
        vc.ID,
        vc.GroupID,
        cast(left(RightString, vc.Position - cte.Position) as varchar(200)) as LeftString,
        vc.Value,
        cast(right(RightString, @StringLen - vc.Position) as varchar(200)) as RightString,
        vc.Position
    from @Chars vc
    join cte cte
        on cte.GroupID = vc.GroupID
            and cte.ID + 1 = vc.ID
    )
    select
        GroupID,
        case
            when LSLenSumMax < @StringLen
                then NewString + RightString
            else NewString
        end as NewString
    from (
        select
            GroupID,
            max(LSLenSum) as LSLenSumMax,
            RightString,
            stuff((
                select
                    LeftString + Value
                from cte cte
                where cte.GroupID = cteLR.GroupID
                for xml path(''), type
                ).value('(./text())[1]', 'varchar(max)'), 1, 0, '') as NewString
        from (
            select
                GroupID,
                (select sum(len(LeftString)) from cte cteL where cteL.groupID = cte.groupID) as LSLenSum,
                (select top 1 RightString from cte cteR where cteR.groupID = cte.groupID order by cteR.ID desc) as RightString
            from cte cte
            ) as cteLR
            group by
                GroupID,
                RightString
            ) as C
    order by GroupID

您可以实施自定义 聚合函数...或者试试这个:递归标量函数:

CREATE FUNCTION dbo.GetPrefixTo (@GroupID INT, @Position INT, @String CHAR(200))
RETURNS Char(200)
WITH EXECUTE AS CALLER
AS
BEGIN
    DECLARE @str CHAR(200) = NULL
    DECLARE @beforePosition INT = 0
    IF (SELECT COUNT(*) FROM Chars WHERE GroupID = @GroupID AND Position < @Position) > 0
    BEGIN
        SELECT @beforePosition = MAX(Position) FROM chars WHERE GroupID = @GroupID AND Position < @Position
        select @str = RTRIM(dbo.GetPrefixTo(@GroupID, @beforePosition, substring(@String, 1, @Position))) + 
            + RTrim(Value) + substring(@String, @Position + 1, len(@string) + 1)
        FROM Chars WHERE GroupID = @GroupID AND Position = @Position
    END
    ELSE
        SELECT @str = substring(@String, 1, @Position) + RTrim(Value) + substring(@String, @Position + 1, len(@string) + 1) FROM Chars
        WHERE GroupID = @GroupID AND Position = @Position
    RETURN @str
END

并按 GroupID 分组并聚合 max(position):

SELECT groupID, max(Position)
    , dbo.GetPrefixTo(groupID, max(Position), 'abcde') 
    FROM Chars 
    GROUP BY GroupID

这是结果:

1           0           Xabcde                                                                                                                                                                                                  
2           2           abYcde                                                                                                                                                                                                  
3           5           abcdeZ                                                                                                                                                                                                  
4           2           XabYcde                                                                                                                                                                                                 
5           5           abYcdeZ                                                                                                                                                                                                 
6           5           XabcdeZ                                                                                                                                                                                                 
7           5           XabYcdeZ                                                                                                                                                                                                
8           4           abY1cdY2e                                                                                                                                                                                               
9           4           XabY1cdY2e                                                                                                                                                                                              
10          5           abY1cdY2eZ                                                                                                                                                                                              
11          5           XabY1cdY2eZ 

============================================= ===========================