SQL Pivoting 问题 - 寻找更好的方法
TSQL Pivoting Issue - looking for better approach
这是一个与 T-SQL 相关的问题。我正在使用 SQL Server 2012。
我有一个 table 这样的:
我想要这样的输出:
解释:
每个员工都有一行。一名员工有一项或多项任务。 Batch Id 指定了这个。根据批次 ID,列名称将发生变化(例如国家 1、国家 2 等)。
到目前为止的方法:
取消旋转源 table,如下所示:
select
EmpId, 'Country ' + cast(BatchId as varchar) as [ColumnName],
Country as [ColumnValue]
from
SourceTable
UNION
select
EmpId, 'Pass ' + cast(BatchId as varchar) as [ColumnName],
Pass as [ColumnValue]
from
SourceTable
它以行的形式给出每一列的值。然后,可以旋转此结果以获得所需的输出。
问题:
- 有更好的方法吗?
- 目前,我知道会有固定数量的批次,但是,对于未来,如果我想让旋转部分动态化,最好的方法是什么?
- 使用 SSIS 或 SSRS 之类的工具,是否更容易动态处理枢轴?
有许多可能的解决方案可以实现您想要的(搜索多列动态数据透视表)
警告:我假设 Country 和 Pass 列不是 NULL
CREATE TABLE SourceTable(EmpId INT, BatchId INT,
Country NVARCHAR(100) NOT NULL, Pass NVARCHAR(5) NOT NULL);
INSERT INTO SourceTable(EmpId, BatchId, Country, Pass)
VALUES
(100, 1, 'UK', 'M'), (200, 2, 'USA', 'U'),
(100, 2, 'Romania', 'M'), (100, 3, 'India', 'MA'),
(100, 4, 'Hongkong', 'MA'), (300, 1, 'Belgium', 'U'),
(300, 2, 'Poland', 'U'), (200, 1, 'Australia', 'M');
/* Get Number of Columns Groups Country1..Country<MaxCount> */
DECLARE @max_count INT
,@sql NVARCHAR(MAX) = ''
,@columns NVARCHAR(MAX) = ''
,@i INT = 0
,@i_s NVARCHAR(10);
WITH cte AS
(
SELECT EmpId
,[cnt] = COUNT(*)
FROM SourceTable
GROUP BY EmpId
)
SELECT @max_count = MAX(cnt)
FROM cte;
WHILE @i < @max_count
BEGIN
SET @i += 1;
SET @i_s = CAST(@i AS NVARCHAR(10));
SET @columns += N',MAX(CASE WHEN [row_no] = ' + @i_s + ' THEN Country END) AS Country' + @i_s +
',MAX(CASE WHEN [row_no] = ' + @i_s + ' THEN Pass END) AS Pass' + @i_s;
END
SELECT @sql =
N';WITH cte AS (
SELECT EmpId, Country, Pass, [row_no] = ROW_NUMBER() OVER (PARTITION BY EmpId ORDER BY BatchId)
FROM SourceTable)
SELECT EmpId ' + @columns + N'
FROM cte
GROUP BY EmpId';
/* Debug */
/* SELECT @sql */
EXEC(@sql);
或者:
DECLARE @cols NVARCHAR(MAX),
@sql NVARCHAR(MAX) = '';
;WITH cte(col_name, rn) AS(
SELECT DISTINCT col_name = col_name + CAST(BatchId AS VARCHAR(10)),
rn = ROW_NUMBER() OVER(PARTITION BY EmpId ORDER BY BatchId)
FROM SourceTable
CROSS APPLY (VALUES ('Country', Country), ('Pass', Pass)) AS c(col_name, val)
)
SELECT @cols = STUFF((SELECT ',' + QUOTENAME(col_name)
FROM cte
ORDER BY rn /* If column order is important for you */
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SET @sql =
N';WITH cte AS
(
SELECT EmpId, col_name = col_name + CAST(BatchId AS VARCHAR(10)), val
FROM SourceTable
CROSS APPLY (VALUES (''Country'', Country), (''Pass'', Pass)) AS c(col_name, val)
)
SELECT *
FROM cte
PIVOT
(
MAX(val)
FOR col_name IN (' + @cols + ')
) piv';
EXEC(@sql);
他妈的在 SQL 做这件事。
让 SSRS 使用 MATRIX 为您完成工作。它将为您提供 PIVOT,而无需创建动态 SQL 来处理需要知道所有列的可怕限制。
对于您的数据,您会将 EMP ID 作为 ROW 组,将 PASS 作为您的列分组。
这是一个与 T-SQL 相关的问题。我正在使用 SQL Server 2012。
我有一个 table 这样的:
我想要这样的输出:
解释:
每个员工都有一行。一名员工有一项或多项任务。 Batch Id 指定了这个。根据批次 ID,列名称将发生变化(例如国家 1、国家 2 等)。
到目前为止的方法:
取消旋转源 table,如下所示:
select
EmpId, 'Country ' + cast(BatchId as varchar) as [ColumnName],
Country as [ColumnValue]
from
SourceTable
UNION
select
EmpId, 'Pass ' + cast(BatchId as varchar) as [ColumnName],
Pass as [ColumnValue]
from
SourceTable
它以行的形式给出每一列的值。然后,可以旋转此结果以获得所需的输出。
问题:
- 有更好的方法吗?
- 目前,我知道会有固定数量的批次,但是,对于未来,如果我想让旋转部分动态化,最好的方法是什么?
- 使用 SSIS 或 SSRS 之类的工具,是否更容易动态处理枢轴?
有许多可能的解决方案可以实现您想要的(搜索多列动态数据透视表)
警告:我假设 Country 和 Pass 列不是 NULL
CREATE TABLE SourceTable(EmpId INT, BatchId INT,
Country NVARCHAR(100) NOT NULL, Pass NVARCHAR(5) NOT NULL);
INSERT INTO SourceTable(EmpId, BatchId, Country, Pass)
VALUES
(100, 1, 'UK', 'M'), (200, 2, 'USA', 'U'),
(100, 2, 'Romania', 'M'), (100, 3, 'India', 'MA'),
(100, 4, 'Hongkong', 'MA'), (300, 1, 'Belgium', 'U'),
(300, 2, 'Poland', 'U'), (200, 1, 'Australia', 'M');
/* Get Number of Columns Groups Country1..Country<MaxCount> */
DECLARE @max_count INT
,@sql NVARCHAR(MAX) = ''
,@columns NVARCHAR(MAX) = ''
,@i INT = 0
,@i_s NVARCHAR(10);
WITH cte AS
(
SELECT EmpId
,[cnt] = COUNT(*)
FROM SourceTable
GROUP BY EmpId
)
SELECT @max_count = MAX(cnt)
FROM cte;
WHILE @i < @max_count
BEGIN
SET @i += 1;
SET @i_s = CAST(@i AS NVARCHAR(10));
SET @columns += N',MAX(CASE WHEN [row_no] = ' + @i_s + ' THEN Country END) AS Country' + @i_s +
',MAX(CASE WHEN [row_no] = ' + @i_s + ' THEN Pass END) AS Pass' + @i_s;
END
SELECT @sql =
N';WITH cte AS (
SELECT EmpId, Country, Pass, [row_no] = ROW_NUMBER() OVER (PARTITION BY EmpId ORDER BY BatchId)
FROM SourceTable)
SELECT EmpId ' + @columns + N'
FROM cte
GROUP BY EmpId';
/* Debug */
/* SELECT @sql */
EXEC(@sql);
或者:
DECLARE @cols NVARCHAR(MAX),
@sql NVARCHAR(MAX) = '';
;WITH cte(col_name, rn) AS(
SELECT DISTINCT col_name = col_name + CAST(BatchId AS VARCHAR(10)),
rn = ROW_NUMBER() OVER(PARTITION BY EmpId ORDER BY BatchId)
FROM SourceTable
CROSS APPLY (VALUES ('Country', Country), ('Pass', Pass)) AS c(col_name, val)
)
SELECT @cols = STUFF((SELECT ',' + QUOTENAME(col_name)
FROM cte
ORDER BY rn /* If column order is important for you */
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SET @sql =
N';WITH cte AS
(
SELECT EmpId, col_name = col_name + CAST(BatchId AS VARCHAR(10)), val
FROM SourceTable
CROSS APPLY (VALUES (''Country'', Country), (''Pass'', Pass)) AS c(col_name, val)
)
SELECT *
FROM cte
PIVOT
(
MAX(val)
FOR col_name IN (' + @cols + ')
) piv';
EXEC(@sql);
他妈的在 SQL 做这件事。
让 SSRS 使用 MATRIX 为您完成工作。它将为您提供 PIVOT,而无需创建动态 SQL 来处理需要知道所有列的可怕限制。
对于您的数据,您会将 EMP ID 作为 ROW 组,将 PASS 作为您的列分组。