SQL 服务器:将行更改为列的最快​​方法

SQL Server : fastest way to change rows to columns

我的数据库中有两个 table 有以下列:

COMMENT table

KEY 
TYPE
NUMBER
TEXT

(KEY, TYPE, NUMBER)

组成的复合键

RESULTS table

KEY
TYPE1
TYPE2
...
TYPE20
TEXT1
TEXT2
...
TEXT20

COMMENT table 的示例如下:

KEY | TYPE | NUMBER | TEXT|
1      A     0001     SAMPLETEXT
1      A     0002     SAMPLETEXT2
1      B     0001     SAMPLETEXT3
1      B     0002     SAMPLETEXT4
1      B     0003     SAMPLETEXT5
2      C     0001     SAMPLETEXT6
2      C     0002     SAMPLETEXT7
3      A     0001     SAMPLETEXT8

对于每个 KEY,只有 3 种不同的类型 A,B,C,而一个 TYPENUMBER 字段中可能最多有 0020。这些记录按 KEY 然后按 TYPE

排序

我需要完成以下操作:对于 COMMENT table 中的每个 KEY,插入第一个 20 TYPE rows into each column inside de RESULTS table (TYPE1 for the first type in the Comment table, TYPE2 for the second type in the comment table 等等)并将前 20 个 TEXT 行插入到 RESULTS table 中的每一列(第一个文本为 TEXT1,第二个文本为 TEXT2,依此类推)

RESULTS table 看起来像这样:

KEY | TYPE1 | TYPE2 | TYPE3 | ... | TYPE20 | TEXT1 | TEXT2 | ... | TEXT20
1      A        A      B             NULL    SAMPTE1 SAMPTE2       NULL
2      C        C      NULL          NULL    SAMPTE6 SAMPTE7       NULL
3      A     NULL      NULL  ...    NULL     SAMPTE7 NULL    ....  NULL

RESULTS table 每个 KEY 都有一行,最多 20 个 TEXT 字段与其相应的类型。

如您所见,这个 RESULTS table 的设计显然很糟糕。它是 70 年代制造的,我们无法更改它。

实施时可能会出现一些问题,这里是答案:

  1. 如果KEYCOMMENTtable中有超过20个TEXT怎么办?我们不管,我们只插入其中的前20个
  2. 例如,如果我有 23 个 A 类文本和 10 个 B 类文本怎么办?然后只有前 20 个类型 A 文本会出现在 RESULTS
  3. 有什么方法可以更改 RESULTS table 吗?可惜没有。
  4. TYPE1是否与RESULTStable和TYPE2[=中的TEXT1匹配88=] 匹配 TEXT2 等等?是的,匹配的列数也是一样的。
  5. 如果COMMENTtable中的TEXT少于20个,则TEXTTYPE[=的其余值结果中的 88=] 为空?是

问题是,什么是最干净、最快、可扩展、不乏味的实现方式?

此外,如何实现它,以便将来 RESULTS table 可以有 N 多 TYPE 列和 N 多 TEXT 列?

我听说过使用 PIVOT table、连接和许多其他技术,但我不知道如何实现。

您可以在 T-SQL 文档中阅读所有关于 pivot 操作的信息。您的答案需要一个双枢轴:一次为每个 [Key,Number] 组合枢轴 Type,一次为每个 [Key,Number] 组合枢轴 Text

为了避免在 Key 列上进行额外的聚合,您可以将这两个数据透视操作拆分为单独的子查询。下面的解决方案将这两个数据透视子查询放在两个 common table expressions (CTE's) 中,分别命名为 piv1piv2。请注意,每个子查询仅从输出所需的那些列开始。此列过滤是使用名为 comm.

的附加子查询完成的

最终查询joins piv1 and piv2 on their common Key column and inserts预定义Result中的输出table.

N TypeText 列的非繁琐方式将需要动态 SQL。 虽然这样的解决方案并不乏味,但它也非常重要! 如果您对 joinpivot 等概念仍然不熟悉,正如您的问题所暗示的那样,那么我强烈建议暂时不要走那条路。复制粘贴某些列名比开发和维护动态 SQL 查询要快得多。

示例数据

create table Comment
(
  [Key] int,
  [Type] nvarchar(1),
  [Number] nvarchar(4), --> changing to numeric type will drop the leading zeros
  [Text] nvarchar(20)
)

insert into Comment ([Key], [Type], [Number], [Text]) values
(1, 'A', '0001', 'SAMPLETEXT'),
(1, 'A', '0002', 'SAMPLETEXT2'),
(1, 'B', '0001', 'SAMPLETEXT3'),
(1, 'B', '0002', 'SAMPLETEXT4'),
(1, 'B', '0003', 'SAMPLETEXT5'),
(2, 'C', '0001', 'SAMPLETEXT6'),
(2, 'C', '0002', 'SAMPLETEXT7'),
(3, 'A', '0001', 'SAMPLETEXT8');

create table Result
(
  [Key] int,
  [Type1] nvarchar(1),
  [Type2] nvarchar(1),
  [Type3] nvarchar(1),
  -- repeat for [Type4] to [Type19]
  [Type20] nvarchar(1),
  [Text1] nvarchar(20),
  [Text2] nvarchar(20),
  [Text3] nvarchar(20),
  -- repeat for [Text4] to [Text19]
  [Text20] nvarchar(20)
);

解决方案

在单独的 CTE 中组合双 pivot,在单个查询语句中组合 joininsert 操作。

with piv1 as
(
  select piv.[Key] as [Key],
         piv.[0001] as [Type1],
         piv.[0002] as [Type2],
         piv.[0003] as [Type3],
         -- repeat for [Type4] to [Type19]
         piv.[0020] as [Type20]
  from ( select c.[Key], c.[Type], c.[Number] from comment as c ) as comm
  pivot (min(comm.[Type]) for comm.[Number] in ([0001], [0002], [0003], [0020])) piv -- add values [0004] to [0019]
),
piv2 as
(
  select piv.[Key] as [Key],
         piv.[0001] as [Text1],
         piv.[0002] as [Text2],
         piv.[0003] as [Text3],
         -- repeat for [Text4] to [Text19]
         piv.[0020] as [Text20]
  from ( select c.[Key], c.[Text], c.[Number] from comment as c ) as comm
  pivot (min(comm.[Text]) for comm.[Number] in ([0001], [0002], [0003], [0020])) piv -- add values [0004] to [0019]
)
insert into Result ([Key],
                    [Type1], [Type2], [Type3], [Type20], -- add columns [Type4] to [Type19]
                    [Text1], [Text2], [Text3], [Text20]) -- add columns [Text4] to [Text19]
select piv1.[Key],
       piv1.[Type1], piv1.[Type2], piv1.[Type3], piv1.[Type20], -- add columns piv1.[Type4] to piv1.[Type19]
       piv2.[Text1], piv2.[Text2], piv2.[Text3], piv2.[Text20]  -- add columns piv2.[Text4] to piv2.[Text19]
from piv1
join piv2 on piv2.[Key] = piv1.[Key];

结果

Key  Type1  Type2  Type3  Type20  Text1        Text2        Text3        Text20
---  -----  -----  -----  ------  -----------  -----------  -----------  -----------
1    A      A      B      null    SAMPLETEXT   SAMPLETEXT2  SAMPLETEXT5  null
2    C      C      null   null    SAMPLETEXT6  SAMPLETEXT7  null         null
3    A      null   null   null    SAMPLETEXT8  null         null         null

Fiddle 查看实际情况。