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

它以行的形式给出每一列的值。然后,可以旋转此结果以获得所需的输出。

问题:

有许多可能的解决方案可以实现您想要的(搜索多列动态数据透视表

SqlFiddleDemo

警告:我假设 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);

或者:

SQLFiddleDemo2

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 作为您的列分组。

https://msdn.microsoft.com/en-us/library/dd207149.aspx