TSQL透视问题 - 寻找更好的方法

时间:2015-08-30 07:53:26

标签: tsql sql-server-2012 pivot dynamic-sql

这是一个与T-SQL相关的问题。我正在使用SQL Server 2012。

我有一张这样的表:

Source Table

我希望输出如下:

Result Table

说明:

对于每位员工,都会有一排。员工有一项或多项任务。 Batch Id指定了这一点。根据批次ID,列名称将更改(例如,国家1,国家2等)。

到目前为止的方法:

取消透视源表,如下所示:

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等工具,动态处理枢轴更容易吗?

2 个答案:

答案 0 :(得分:2)

在SQL中执行它。

让SSRS通过 MATRIX 为您完成工作。它将为您提供PIVOT,而无需创建动态SQL来处理需要知道所有列的可怕限制。

对于您的数据,您可以将EMP ID作为ROW组,并将PASS作为列分组。

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

答案 1 :(得分:1)

有许多可能的解决方案可以实现您的目标(在多列上搜索 Dynamic Pivot

<强> 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);