如何解决需要将列分为行和列
我有一张这样的桌子:
ID cst
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
etc.
现在,我想将cst列分为5列和多行。 像这样:
ID C1 C2 C3 C4 C5
1 string1 3 string2 string3 34
1 string4 -1 string5 string6 12
1 string7 5 string8 string9 65
2 string10 -3 string11 string12 56
2 string13 6 string14 string15 9
etc.
如何做到这一点?我在sql Server 2017中,因此可以使用string_split函数。此函数的问题在于它仅产生一个输出列...
最好我想创建一个输出表的UDF。该函数将使用以下输入参数:字符串,分隔符,列数。因此,该函数可以动态地用于不同数量的列。
ps。字符串的长度当然可以是可变的。
解决方法
老实说,这里最简单的选择可能是以下步骤:
- 使用分号作为分隔符(也是当前
cst
列的分隔符,将当前表写到CSV平面文件中 - 然后使用SQL Server的批量加载工具加载CSV,再次使用分号作为列分隔符。这将产生一个包含16列
ID
,然后C1
到C15
并包括(ID,C1,C2,C3,C4,C5)
的表。 - 创建一个新表
INSERT INTO newTable (ID,C5) SELECT ID,C5 FROM loadedTable UNION ALL SELECT ID,C6,C7,C8,C9,C10 FROM loadedTable UNION ALL SELECT ID,C11,C12,C13,C14,C15 FROM loadedTable;
然后使用以下命令填充上表:
bazel info
尽管上述建议似乎需要大量工作,但SQL Server对正则表达式和复杂的字符串拆分操作的支持不佳,尤其是在早期版本上。可能无法直接使用当前表进行操作,或者可能无法完成以上工作。
,尝试以下方法:
提示:示例数据中有一些“普通”逗号。 我怀疑这些是错误的,并使用了分号。 如果这是错误的,则可以使用常规的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,请在下面的UPDATE中找到v2017 +)
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
子句的 tally 。它将返回与XML中的元素一样多的虚拟行。 - 我们使用
sql:column()
来获取每个元素的位置值,并使用一些简单的数学方法来创建分组键和0到4之间的一个连续数字,依此类推。 - 我们将
GROUP BY
与MAX(CASE...)
一起使用,将值放在拟合列中(老式枢轴或条件聚合)。 / li>
提示:如果要完全通用地使用,则不事先知道一些列。您不能使用任何类型的功能或即席查询。您宁愿在存储过程中与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('["','"]')) A
)
SELECT ID,GroupingKey;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。