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
,而一个 TYPE
在 NUMBER
字段中可能最多有 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 年代制造的,我们无法更改它。
实施时可能会出现一些问题,这里是答案:
- 如果KEY在
COMMENT
table中有超过20个TEXT怎么办?我们不管,我们只插入其中的前20个
- 例如,如果我有 23 个 A 类文本和 10 个 B 类文本怎么办?然后只有前 20 个类型 A 文本会出现在
RESULTS
- 有什么方法可以更改
RESULTS
table 吗?可惜没有。
- TYPE1是否与
RESULTS
table和TYPE2[=中的TEXT1匹配88=] 匹配 TEXT2 等等?是的,匹配的列数也是一样的。
- 如果
COMMENT
table中的TEXT少于20个,则TEXT和TYPE[=的其余值结果中的 88=] 为空?是
问题是,什么是最干净、最快、可扩展、不乏味的实现方式?
此外,如何实现它,以便将来 RESULTS table 可以有 N 多 TYPE
列和 N 多 TEXT
列?
我听说过使用 PIVOT table、连接和许多其他技术,但我不知道如何实现。
您可以在 T-SQL 文档中阅读所有关于 pivot 操作的信息。您的答案需要一个双枢轴:一次为每个 [Key,Number]
组合枢轴 Type
,一次为每个 [Key,Number]
组合枢轴 Text
。
为了避免在 Key
列上进行额外的聚合,您可以将这两个数据透视操作拆分为单独的子查询。下面的解决方案将这两个数据透视子查询放在两个 common table expressions (CTE's) 中,分别命名为 piv1
和 piv2
。请注意,每个子查询仅从输出所需的那些列开始。此列过滤是使用名为 comm
.
的附加子查询完成的
最终查询joins piv1
and piv2
on their common Key
column and inserts预定义Result
中的输出table.
N Type
或 Text
列的非繁琐方式将需要动态 SQL。 虽然这样的解决方案并不乏味,但它也非常重要! 如果您对 join
和 pivot
等概念仍然不熟悉,正如您的问题所暗示的那样,那么我强烈建议暂时不要走那条路。复制粘贴某些列名比开发和维护动态 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
,在单个查询语句中组合 join
和 insert
操作。
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 查看实际情况。
我的数据库中有两个 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
,而一个 TYPE
在 NUMBER
字段中可能最多有 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 年代制造的,我们无法更改它。
实施时可能会出现一些问题,这里是答案:
- 如果KEY在
COMMENT
table中有超过20个TEXT怎么办?我们不管,我们只插入其中的前20个 - 例如,如果我有 23 个 A 类文本和 10 个 B 类文本怎么办?然后只有前 20 个类型 A 文本会出现在
RESULTS
- 有什么方法可以更改
RESULTS
table 吗?可惜没有。 - TYPE1是否与
RESULTS
table和TYPE2[=中的TEXT1匹配88=] 匹配 TEXT2 等等?是的,匹配的列数也是一样的。 - 如果
COMMENT
table中的TEXT少于20个,则TEXT和TYPE[=的其余值结果中的 88=] 为空?是
问题是,什么是最干净、最快、可扩展、不乏味的实现方式?
此外,如何实现它,以便将来 RESULTS table 可以有 N 多 TYPE
列和 N 多 TEXT
列?
我听说过使用 PIVOT table、连接和许多其他技术,但我不知道如何实现。
您可以在 T-SQL 文档中阅读所有关于 pivot 操作的信息。您的答案需要一个双枢轴:一次为每个 [Key,Number]
组合枢轴 Type
,一次为每个 [Key,Number]
组合枢轴 Text
。
为了避免在 Key
列上进行额外的聚合,您可以将这两个数据透视操作拆分为单独的子查询。下面的解决方案将这两个数据透视子查询放在两个 common table expressions (CTE's) 中,分别命名为 piv1
和 piv2
。请注意,每个子查询仅从输出所需的那些列开始。此列过滤是使用名为 comm
.
最终查询joins piv1
and piv2
on their common Key
column and inserts预定义Result
中的输出table.
N Type
或 Text
列的非繁琐方式将需要动态 SQL。 虽然这样的解决方案并不乏味,但它也非常重要! 如果您对 join
和 pivot
等概念仍然不熟悉,正如您的问题所暗示的那样,那么我强烈建议暂时不要走那条路。复制粘贴某些列名比开发和维护动态 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
,在单个查询语句中组合 join
和 insert
操作。
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 查看实际情况。