我正在寻找“取消旋转”表格,但我不确定最好的方法是什么。此外,这些值由“;”分隔。我列出了我正在查看的示例:
Column_A | Column_B | Column_C | Column_D |
---|---|---|---|
000 | A;B;C;D | 01;02;03;04 | X;Y;D;E |
001 | A;B | 05;06 | S;T |
002 | C | 07 | S |
从那以后,我正在寻找一种方法来取消它,但同时也要保持它当前所处的关系。例如,Column_B、C 和 D 中的第一个值被绑定在一起: |列_A|列_B|列_C|列_D| |:-|:-|:-|:-| |000|A|01|X| |000|B|02|Y| |000|C|03|D| |000|D|04|E| |001|A|05|S|
等等。
我最初的想法是使用 CTE,我已将其设置为:
WITH TEST AS(
SELECT DISTINCT Column_A, Column_B, Column_C, VALUE AS Column_D
from [TABLE]
CROSS APPLY STRING_SPLIT(Column_D, ';'))
SELECT \* FROM TEST
;
虽然这似乎不会产生正确的结果,尤其是在堆叠 CTE 和字符串拆分之后。
作为更新,下面提供了非常有用的解决方案。它们都按预期运行,但是我最后添加了一个。如果一行/列是空白的,是否可以/合理地忽略它?例如,跳过 Column_C,其中 Column_A 是“001”。 |列_A|列_B|列_C|列_D| |:-|:-|:-|:-| |000|A;B;C;D|01;02;03;04|X;Y;D;E| |001|A;B||S;T| |002|C|07|S|
答案 0 :(得分:1)
您可以使用递归 CTE 来遍历字符串。假设它们的长度都相同(即分号数相同):
with cte as (
select a, convert(varchar(max), null) as b, convert(varchar(max), null) as c, convert(varchar(max), null) as d,
convert(varchar(max), b + ';') as rest_b, convert(varchar(max), c + ';') as rest_c, convert(varchar(max), d + ';') as rest_d,
0 as lev
from t
union all
select a,
left(rest_b, charindex(';', rest_b) - 1),
left(rest_c, charindex(';', rest_c) - 1),
left(rest_d, charindex(';', rest_d) - 1),
stuff(rest_b, 1, charindex(';', rest_b), ''),
stuff(rest_c, 1, charindex(';', rest_c), ''),
stuff(rest_d, 1, charindex(';', rest_d), ''),
lev + 1
from cte
where rest_b <> ''
)
select a, b, c, d
from cte
where lev > 0
order by a, lev;
Here 是一个 db<>fiddle。
编辑:
您可以使用以下方法确保过滤器使用具有相同分号数的行:
where (length(b) - length(replace(b, ';', ''))) = (length(c) - length(replace(c, ';', ''))) and
(length(b) - length(replace(b, ';', ''))) = (length(d) - length(replace(d, ';', '')))
您还可以使用一堆分号扩展 c
和 d
,这样就不会发生错误并且结果值为空字符串。这些列中的额外分号无关紧要,因此您可以使用:
select a, convert(varchar(max), null) as b, convert(varchar(max), null) as c, convert(varchar(max), null) as d,
convert(varchar(max), b + ';') as rest_b, convert(varchar(max), c + replicate(';', length(b))) as rest_c, convert(varchar(max), d + replicate(';', length(b))) as rest_d,
0 as lev
答案 1 :(得分:1)
这是一个基于 JSON 的方法。 SQL Server 2016 及更高版本。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ColA varchar(3), ColB varchar(8000), ColC varchar(8000), ColD varchar(8000));
INSERT INTO @tbl VALUES
('000','A;B;C;D','01;02;03;04','X;Y;D;E'),
('001','A;B','05;06','S;T'),
('002','C','07','S');
-- DDL and sample data population, end
WITH rs AS
(
SELECT *
, ar1 = '["' + REPLACE(ColB, ';', '","') + '"]'
, ar2 = '["' + REPLACE(ColC, ';', '","') + '"]'
, ar3 = '["' + REPLACE(ColD, ';', '","') + '"]'
FROM @tbl
)
SELECT ColA, ColB.[value] AS [ColB], ColC.[value] AS ColC, ColD.[value] AS ColD
FROM rs
CROSS APPLY OPENJSON (ar1, N'$') AS ColB
CROSS APPLY OPENJSON (ar2, N'$') AS ColC
CROSS APPLY OPENJSON (ar3, N'$') AS ColD
WHERE ColB.[key] = ColC.[key]
AND ColB.[key] = ColD.[key];
输出
+------+------+------+------+
| ColA | ColB | ColC | ColD |
+------+------+------+------+
| 000 | A | 01 | X |
| 000 | B | 02 | Y |
| 000 | C | 03 | D |
| 000 | D | 04 | E |
| 001 | A | 05 | S |
| 001 | B | 06 | T |
| 002 | C | 07 | S |
+------+------+------+------+
答案 2 :(得分:0)
这里真正的问题是你的设计。 希望您这样做的原因是为了修复您的设计。
很遗憾,您不能为此使用 SQL Server 的内置 STRING_SPLIT
,因为它不提供序数位置。因此,我使用 DelimitedSplit8K_LEAD
将值分隔成行,然后“加入”然后备份。这确实假设所有列都具有相同数量的分隔值。
CREATE TABLE dbo.YourTable (ColA varchar(3),
ColB varchar(8000),
ColC varchar(8000),
ColD varchar(8000));
INSERT INTO dbo.YourTable
VALUES('000','A;B;C;D','01;02;03;04','X;Y;D;E'),
('001','A;B','05;06','S;T'),
('002','C','07','S');
GO
SELECT YT.ColA,
DSLB.Item AS ColB,
DSLC.Item AS ColC,
DSLD.Item AS ColD
FROM dbo.YourTable YT
CROSS APPLY dbo.DelimitedSplit8K_LEAD(YT.ColB,';') DSLB
CROSS APPLY dbo.DelimitedSplit8K_LEAD(YT.ColC,';') DSLC
CROSS APPLY dbo.DelimitedSplit8K_LEAD(YT.ColD,';') DSLD
WHERE DSLB.ItemNumber = DSLC.ItemNumber
AND DSLC.ItemNumber = DSLD.ItemNumber;
GO
DROP TABLE dbo.YourTable;