将不确定数量的行转置为列?
Transpose indeterminate number of rows into columns?
我有两个带数据的table,我们称它们为table T和table B。T和B中的相关数据与每个数据都是多对一的关系其他,像这样:
T.id
B.code
A
1
A
1.2
B
1.2
B
1.5
C
1
C
2
C
3
C
15
D
296
等(这只是一个粗略的演示,与我使用的实际数据不同)。
我运行一个count(*)查询,发现table T中的项目出现在table B中的次数最多是8次。我想要的是将上面的 table 转置为 9 列 table,其中 T 中有成千上万个条目,B 的相应值有数百个可能的条目,如下所示:
T.id
CODE 1
CODE 2
CODE 3
CODE 4
CODE 5
CODE 6
CODE 7
CODE 8
A
1
1.2
NULL
NULL
NULL
NULL
NULL
NULL
B
1.2
1.5
NULL
NULL
NULL
NULL
NULL
NULL
C
1
2
3
15
NULL
NULL
NULL
NULL
D
296
NULL
NULL
NULL
NULL
NULL
NULL
NULL
等等
我能找到的唯一搜索信息需要每次手动加入 B 并排除 B.code 的先前值(正如我目前所做的那样),或者使用 PIVOT table(动态SQL 或其他),对于 B.code 的每个可能值(如前所述,有数百个值),它们都必须有一列,这两者都不是可扩展的选项。
作为参考,我的代码现在看起来像这样:
SELECT
T.id,
b1.code as 'CODE 1', b2.code as 'CODE 2', b3.code as 'CODE 3', b4.code as 'CODE 4' (etc)
FROM T
LEFT JOIN B b1 ON b1.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id)
LEFT JOIN B b2 ON b2.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code))
LEFT JOIN B b3 ON b3.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code, b2.code))
LEFT JOIN B b4 ON b4.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code, b2.code, b3.code))
我有哪些选择?我注定了吗?一定有更好的方法吧?
未经测试,但也许这会有所帮助
Select *
From (
Select T.id
,B.Code
,Col = concat('Code ',row_number() over (partition by T.id order by B.code) )
From T
Join B on B.ID=T.ID
) src
Pivot (max(Code) for Col in ([Code 1],
[Code 2],
[Code 3],
[Code 4],
[Code 5],
[Code 6],
[Code 7],
[Code 8]) ) pvt
您想要结束的查询是:
;WITH x AS
(
SELECT [T.id], Code, rn = ROW_NUMBER() OVER
(PARTITION BY [T.id] ORDER BY @@SPID)
FROM dbo.B
) SELECT [T.id], [CODE 1] = p.[1],[CODE 2] = p.[2], ...
FROM x
PIVOT (MAX(Code) FOR rn IN
([1],[2],...)) AS p;
但是您需要动态 SQL 才能到达那里,而不必硬编码 ...Code 8
或在另一个代码添加到 table 时添加 Code 9
:
DECLARE @sql nvarchar(max) = N'',
@outputColumns nvarchar(max),
@pivotColumns nvarchar(max),
@maxCount int = (SELECT MAX(c) FROM
(SELECT COUNT(*) FROM dbo.B GROUP BY [T.id]) AS c(c));
;WITH x(n) AS (SELECT 1 UNION ALL SELECT n+1
FROM x WHERE n < @maxCount)
SELECT @outputColumns = STRING_AGG(CONCAT('[CODE ',n,
'] = p.',QUOTENAME(n)), ','),
@pivotColumns = STRING_AGG(QUOTENAME(n), N',')
FROM x;
SELECT @sql = N';WITH x AS
(
SELECT [T.id], Code, rn = ROW_NUMBER() OVER
(PARTITION BY [T.id] ORDER BY @@SPID)
FROM dbo.B
) SELECT [T.id], ' + @outputColumns + N'
FROM x
PIVOT (MAX(Code) FOR rn IN
(' + @pivotColumns + N')) AS p;';
EXEC sys.sp_executesql @sql;
输出(来自问题中的样本数据):
T.id
CODE 1
CODE 2
CODE 3
CODE 4
A
1
1.2
null
null
B
1.2
1.5
null
null
C
1
2
3
15
D
296
null
null
null
我有两个带数据的table,我们称它们为table T和table B。T和B中的相关数据与每个数据都是多对一的关系其他,像这样:
T.id | B.code |
---|---|
A | 1 |
A | 1.2 |
B | 1.2 |
B | 1.5 |
C | 1 |
C | 2 |
C | 3 |
C | 15 |
D | 296 |
等(这只是一个粗略的演示,与我使用的实际数据不同)。
我运行一个count(*)查询,发现table T中的项目出现在table B中的次数最多是8次。我想要的是将上面的 table 转置为 9 列 table,其中 T 中有成千上万个条目,B 的相应值有数百个可能的条目,如下所示:
T.id | CODE 1 | CODE 2 | CODE 3 | CODE 4 | CODE 5 | CODE 6 | CODE 7 | CODE 8 |
---|---|---|---|---|---|---|---|---|
A | 1 | 1.2 | NULL | NULL | NULL | NULL | NULL | NULL |
B | 1.2 | 1.5 | NULL | NULL | NULL | NULL | NULL | NULL |
C | 1 | 2 | 3 | 15 | NULL | NULL | NULL | NULL |
D | 296 | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
等等
我能找到的唯一搜索信息需要每次手动加入 B 并排除 B.code 的先前值(正如我目前所做的那样),或者使用 PIVOT table(动态SQL 或其他),对于 B.code 的每个可能值(如前所述,有数百个值),它们都必须有一列,这两者都不是可扩展的选项。
作为参考,我的代码现在看起来像这样:
SELECT
T.id,
b1.code as 'CODE 1', b2.code as 'CODE 2', b3.code as 'CODE 3', b4.code as 'CODE 4' (etc)
FROM T
LEFT JOIN B b1 ON b1.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id)
LEFT JOIN B b2 ON b2.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code))
LEFT JOIN B b3 ON b3.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code, b2.code))
LEFT JOIN B b4 ON b4.code =
(SELECT TOP 1 code FROM B WHERE B.t_id = T.id AND B.code NOT IN (b1.code, b2.code, b3.code))
我有哪些选择?我注定了吗?一定有更好的方法吧?
未经测试,但也许这会有所帮助
Select *
From (
Select T.id
,B.Code
,Col = concat('Code ',row_number() over (partition by T.id order by B.code) )
From T
Join B on B.ID=T.ID
) src
Pivot (max(Code) for Col in ([Code 1],
[Code 2],
[Code 3],
[Code 4],
[Code 5],
[Code 6],
[Code 7],
[Code 8]) ) pvt
您想要结束的查询是:
;WITH x AS
(
SELECT [T.id], Code, rn = ROW_NUMBER() OVER
(PARTITION BY [T.id] ORDER BY @@SPID)
FROM dbo.B
) SELECT [T.id], [CODE 1] = p.[1],[CODE 2] = p.[2], ...
FROM x
PIVOT (MAX(Code) FOR rn IN
([1],[2],...)) AS p;
但是您需要动态 SQL 才能到达那里,而不必硬编码 ...Code 8
或在另一个代码添加到 table 时添加 Code 9
:
DECLARE @sql nvarchar(max) = N'',
@outputColumns nvarchar(max),
@pivotColumns nvarchar(max),
@maxCount int = (SELECT MAX(c) FROM
(SELECT COUNT(*) FROM dbo.B GROUP BY [T.id]) AS c(c));
;WITH x(n) AS (SELECT 1 UNION ALL SELECT n+1
FROM x WHERE n < @maxCount)
SELECT @outputColumns = STRING_AGG(CONCAT('[CODE ',n,
'] = p.',QUOTENAME(n)), ','),
@pivotColumns = STRING_AGG(QUOTENAME(n), N',')
FROM x;
SELECT @sql = N';WITH x AS
(
SELECT [T.id], Code, rn = ROW_NUMBER() OVER
(PARTITION BY [T.id] ORDER BY @@SPID)
FROM dbo.B
) SELECT [T.id], ' + @outputColumns + N'
FROM x
PIVOT (MAX(Code) FOR rn IN
(' + @pivotColumns + N')) AS p;';
EXEC sys.sp_executesql @sql;
输出(来自问题中的样本数据):
T.id CODE 1 CODE 2 CODE 3 CODE 4 A 1 1.2 null null B 1.2 1.5 null null C 1 2 3 15 D 296 null null null