2014-02-24 75 views
1

我使用带有动态创建列的数据透视表来创建按月计算操作的报表。在动态查询中是一个涉及三列的CROSS APPLY。这导致我的总数比他们应该多三倍。我目前正在破解一个解决方案,将计数除以三,以得到正确的答案。任何人都可以帮助我想出一个更优雅的解决方案来解决这个问题吗?SQL数据透视表交叉应用于计数结果

编辑:我使用SQL Server 2008R2

鉴于此数据集(它实际上是一个看法,但我不觉得这是明智的做法是重新创建所以整个架构):

CREATE TABLE vw_ActionsReport 
    ([CID] int, [MitigationActionID] int, [Approved] int, [Status] varchar(11), [ChangedDate] datetime, [EntryDate] varchar(7), [STATE_ABBR] varchar(2), [STATE_NAME] varchar(11), [CENSUS_NAM] varchar(12), [CIS_NAME] varchar(21), [COUNTY_NAM] varchar(9), [CO_FIPS] int, [REGION] int, [ST_FIPS] int); 

INSERT INTO vw_ActionsReport 
    ([CID], [MitigationActionID], [Approved], [Status], [ChangedDate], [EntryDate], [STATE_ABBR], [STATE_NAME], [CENSUS_NAM], [CIS_NAME], [COUNTY_NAM], [CO_FIPS], [REGION], [ST_FIPS]) 
VALUES 
    (090069, 5475, 1, 'Identified', '2012-11-27 16:21:27', '11_2012', 'CT', 'CONNECTICUT', 'OLD SAYBROOK', 'OLD SAYBROOK, TOWN OF', 'MIDDLESEX', 09007, 01, 09), 
    (090069, 5476, 1, 'In Progress', '2012-11-27 16:21:27', '11_2012', 'CT', 'CONNECTICUT', 'OLD SAYBROOK', 'OLD SAYBROOK, TOWN OF', 'MIDDLESEX', 09007, 01, 09), 
    (090012, 6687, 1, 'Identified', '2013-04-02 16:14:03', '4_2013', 'CT', 'CONNECTICUT', 'NORWALK', 'NORWALK, CITY OF', 'FAIRFIELD', 09001, 01, 09), 
    (090008, 6993, 1, 'Identified', '2013-06-20 15:18:38', '6_2013', 'CT', 'CONNECTICUT', 'GREENWICH', 'GREENWICH, TOWN OF', 'FAIRFIELD', 09001, 01, 09), 
    (090019, 17000, 0, 'Identified', '2013-11-26 11:46:14', '11_2013', 'CT', 'CONNECTICUT', 'WESTPORT', 'WESTPORT, TOWN OF', 'FAIRFIELD', 09001, 01, 09); 

然后,使用下面的查询:

DECLARE @cols AS NVARCHAR(MAX), 
     @cols_math AS NVARCHAR(MAX), 
     @query AS NVARCHAR(MAX); 

SELECT @cols = STUFF 
     (
      (
      SELECT ',' + QUOTENAME(EntryDate) 
      FROM dbo.vw_ActionsReport 
      GROUP BY EntryDate, 
        DATEPART(YEAR, dbo.vw_ActionsReport.ChangedDate), 
        DATEPART(MONTH, dbo.vw_ActionsReport.ChangedDate) 
      ORDER BY DATEPART(YEAR, dbo.vw_ActionsReport.ChangedDate), 
        DATEPART(MONTH, dbo.vw_ActionsReport.ChangedDate) 
      FOR XML PATH(''), TYPE 
     ).value('.', 'NVARCHAR(MAX)'), 
      1,1,'' 
     ); 

SELECT @cols_math = STUFF 
     (
      (
      -- This is my hack where I divide the answer by three 
      SELECT ',' + QUOTENAME(EntryDate) + '/3 AS ' + QUOTENAME(EntryDate) 
      FROM dbo.vw_ActionsReport 
      GROUP BY EntryDate, 
        DATEPART(YEAR, dbo.vw_ActionsReport.ChangedDate), 
        DATEPART(MONTH, dbo.vw_ActionsReport.ChangedDate) 
      ORDER BY DATEPART(YEAR, dbo.vw_ActionsReport.ChangedDate), 
        DATEPART(MONTH, dbo.vw_ActionsReport.ChangedDate) 
      FOR XML PATH(''), TYPE 
     ).value('.', 'NVARCHAR(MAX)'), 
      1,1,'' 
     ); 

SET @query = 'SELECT REGION, STATE_ABBR, [Status],' + @cols_math + ' FROM 
      (
       SELECT REGION, STATE_ABBR, [Status], EntryDate, MitigationActionID 
       FROM dbo.vw_ActionsReport 
       CROSS APPLY 
       (
        SELECT CAST(REGION AS VARCHAR(50)) UNION ALL 
        SELECT CAST(STATE_ABBR AS VARCHAR(50)) UNION ALL 
        SELECT CAST([Status] AS VARCHAR(50)) 
       ) c(col) 
      ) x 
      PIVOT 
      (
       COUNT(MitigationActionID) 
       FOR EntryDate IN (' + @cols + ') 
      ) p '; 

EXECUTE(@query); 

输出应该是这样的:

+-REGION-+-STATE_ABBR-+-Status------+-11_2012-+-4_2013-+-6_2013-+-11_2013-+ 
| 01  | CT   | Identified | 1  | 1  | 1  | 1  | 
| 01  | CT   | In Progress | 1  | 0  | 0  | 0  | 

如果我没有在上面的查询中的评论中显示我的破解,那么输出表中的所有1都变为3。我很确定它来自CROSS APPLY,因为如果我改变CROSS APPLY中SELECTS的数量,总数也会以同样的方式改变。

我确定有更好的方法来做到这一点,但我一直不成功。我花了相当多的时间试图让DISTINCT工作,但我似乎无法使用COUNT DISTINCT从PIVOT内部工作。

如果有人能就此给我任何建议,我将不胜感激。

+0

'COUNT(DISTINCT MitigationActionID)'是否有效? – NickyvV

+0

@NickyvV,不,我花了很多时间试图让它工作,但找不到解决方案。我在关键字'DISTINCT'.'附近获得了错误的语法 – Matthew

回答

6

问题是你正在申请CROSS APPLY,你不需要。通常情况下,如果您需要在多个列上旋转,但是您只旋转一列,那么不需要使用不透明转换,则可以使用CROSS APPLY或UNPIVOT。

下面是该查询应该是什么样子:

SELECT region, STATE_ABBR, 
    [Status], 
    [11_2012], [4_2013], [6_2013], [11_2013] 
FROM 
(
    SELECT REGION, STATE_ABBR, 
    [Status], EntryDate, 
    MitigationActionID 
    FROM dbo.vw_ActionsReport 
) x 
PIVOT 
(
    COUNT(MitigationActionID) 
    FOR EntryDate IN ([11_2012], [4_2013], [6_2013], [11_2013]) 
) p; 

SQL Fiddle with Demo

然后动态SQL代码如下:

DECLARE @cols AS NVARCHAR(MAX), 
     @query AS NVARCHAR(MAX) 

SELECT @cols = STUFF 
     (
      (
      SELECT ',' + QUOTENAME(EntryDate) 
      FROM dbo.vw_ActionsReport 
      GROUP BY EntryDate, 
        DATEPART(YEAR, dbo.vw_ActionsReport.ChangedDate), 
        DATEPART(MONTH, dbo.vw_ActionsReport.ChangedDate) 
      ORDER BY DATEPART(YEAR, dbo.vw_ActionsReport.ChangedDate), 
        DATEPART(MONTH, dbo.vw_ActionsReport.ChangedDate) 
      FOR XML PATH(''), TYPE 
     ).value('.', 'NVARCHAR(MAX)'), 
      1,1,'' 
     ); 

SET @query = 'SELECT REGION, STATE_ABBR, [Status],' + @cols + ' 
       FROM 
       (
       SELECT REGION, STATE_ABBR, 
        [Status], EntryDate, 
        MitigationActionID 
       FROM dbo.vw_ActionsReport 
      ) x 
      PIVOT 
      (
       COUNT(MitigationActionID) 
       FOR EntryDate IN (' + @cols + ') 
      ) p '; 

EXECUTE(@query); 

SQL Fiddle with Demo。通过使用此代码,您将得到如下结果:

| REGION | STATE_ABBR |  STATUS | 11_2012 | 4_2013 | 6_2013 | 11_2013 | 
|--------|------------|-------------|---------|--------|--------|---------| 
|  1 |   CT | Identified |  1 |  1 |  1 |  1 | 
|  1 |   CT | In Progress |  1 |  0 |  0 |  0 |