首先,阅读以下文章,他们讨论了拆分字符串(以及不这样做的原因),并比较了在无法避免的情况下进行拆分的方法的性能。
这三篇文章的结果是(以防链接失效):
- 尽可能避免将分隔列表作为字符串,如果您需要存储列表,表格是更好的选择。
- 如果必须这样做,CLR 是最可扩展(且最准确的方法)。
- 如果您可以确定没有要拆分的特殊 XML 字符,则将分隔的字符串转换为 XML,然后使用 XQuery 来获取各个项目的效果很好。
- 否则,最好使用交叉连接构建计数表。
最通用的方法是最后一种,因为不是每个人都可以使用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, '');