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
============================================= ===========================
我想在给定字符串的指定位置插入一组值。我找不到现有的解决方案,所以我创建了以下查询。我想知道是否有一种方法可以简化它,尤其是查询 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
============================================= ===========================