2016-06-27 4 views
0

Я хочу вставить набор значений в заданную строку в указанных положениях. Я не мог найти существующее решение для этого, поэтому я создал следующий запрос, который делает это. Мне интересно, есть ли способ упростить его, особенно часть, которая запрашивает cte и создает NewString. Кроме того, есть ли способ построить рекурсию cte без привлечения столбца ID? Идентификатор (и остальное), кажется, работает нормально, просто интересно, можно ли его настроить, чтобы сделать его более органичным/элегантным или если есть лучшее решение в целом. Различные группы действительно предназначены только для тестирования; каждая группа соответствует возможному сценарию положения вставки. Таким образом, при использовании я не буду участвовать в группах.SQL Server: вставка строки по положению

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 
+0

Я не знаю пути, но пару дней назад я нашел действительно классную статью о n-граммах, которая могла бы помочь: http://www.sqlservercentral.com/articles/Tally+Table/142316/ – vercelli

ответ

0

Вы можете реализовать функцию совокупного пользовательского ... Или попробуйте это: рекурсивная функция скалярной:

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 и совокупным макс (позиция):

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 

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

Смежные вопросы