将不确定数量的行转置为列?

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