试试这个:
提示:您的示例数据中有一些“正常”逗号。
我怀疑这些是错误的并使用了分号。
如果这是错误的,您可以使用一般的 REPLACE() 来使用“;”而不是“,”。
创建一个声明的表来模拟您的问题
DECLARE @tbl TABLE(ID INT, cst VARCHAR(1000));
INSERT INTO @tbl(ID,cst)
VALUES(1,'string1;3;string2;string3;34;string4;-1;string5;string6;12;string7;5;string8;string9; 65')
,(2,'string10;-3;string11;string12;56;string13;6;string14;string15;9');
--查询(对于几乎所有版本的 SQL-Server,请在下面找到 v2017+ 作为 UPDATE)
WITH cte AS
(
SELECT t.ID
,B.Nr
,A.Casted.value('(/x[sql:column("B.Nr")]/text())[1]','varchar(max)') AS ValueAtPosition
,(B.Nr-1) % 5 AS Position
,(B.Nr-1)/5 AS GroupingKey
FROM @tbl t
CROSS APPLY(SELECT CAST('<x>' + REPLACE(t.cst,';','</x><x>') + '</x>' AS XML)) A(Casted)
CROSS APPLY(SELECT TOP(A.Casted.value('count(x)','int')) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) FROM master..spt_values) B(Nr)
)
SELECT ID
,GroupingKey
,MAX(CASE WHEN Position=0 THEN ValueAtPosition END) AS C1
,MAX(CASE WHEN Position=1 THEN ValueAtPosition END) AS C2
,MAX(CASE WHEN Position=2 THEN ValueAtPosition END) AS C3
,MAX(CASE WHEN Position=3 THEN ValueAtPosition END) AS C4
,MAX(CASE WHEN Position=4 THEN ValueAtPosition END) AS C5
FROM cte
GROUP BY ID,GroupingKey
ORDER BY ID,GroupingKey;
简而言之:
- 我们使用
APPLY 将转换为XML 的字符串添加到结果集中。这将有助于拆分字符串 ("a;b;c" => <x>a</x><x>b</x><x>c</x>)
- 我们使用另一个
APPLY 来创建一个动态提示,其中包含一个计算出的TOP 子句。它将返回与 XML 中的元素一样多的虚拟行
- 我们使用
sql:column() 根据每个元素的位置和一些简单的数学运算来获取每个元素的值,以创建分组键和从 0 到 4 的编号等。
- 我们使用
GROUP BY 和MAX(CASE...) 将值放置在拟合列中(老式枢轴 或条件聚合)。
提示:如果你想要这个完全通用的,有很多列是事先不知道的。您不能使用任何类型的函数或临时查询。您宁愿需要在存储过程中创建某种动态语句以及 EXEC。
老实说:这可能是 XY 问题。这种方法是错误的想法——至少在我能想到的几乎所有情况下都是这样。
SQL-Server 2017+ 更新
您使用的是 v2017,这允许使用 JSON,这在 位置安全 字符串拆分方面要快一些。试试这个:
SELECT t.ID
,A.*
FROM @tbl t
CROSS APPLY OPENJSON(CONCAT('["',REPLACE(t.cst,';','","'),'"]')) A
总体思路是一样的。我们将字符串转换为 JSON 数组 ("a,b,c" => ["a","b","c"]) 并使用 APPLY OPENJSON() 读取它。
您可以在“关键”列执行相同的数学运算,然后按照上述方式完成其余的操作。
正因为这里已经准备好了,所以这是v2017+的完整查询
WITH cte AS
(
SELECT t.ID
,A.[key]+1 AS Nr
,A.[value] AS ValueAtPosition
,A.[key] % 5 AS Position
,A.[key]/5 AS GroupingKey
FROM @tbl t
CROSS APPLY OPENJSON(CONCAT('["',REPLACE(t.cst,';','","'),'"]')) A
)
SELECT ID
,GroupingKey
,MAX(CASE WHEN Position=0 THEN ValueAtPosition END) AS C1
,MAX(CASE WHEN Position=1 THEN ValueAtPosition END) AS C2
,MAX(CASE WHEN Position=2 THEN ValueAtPosition END) AS C3
,MAX(CASE WHEN Position=3 THEN ValueAtPosition END) AS C4
,MAX(CASE WHEN Position=4 THEN ValueAtPosition END) AS C5
FROM cte
GROUP BY ID,GroupingKey
ORDER BY ID,GroupingKey;