2014-02-24 2 views
1

동적으로 생성 된 열이있는 피벗 테이블을 사용하여 월별 작업을 계산하는 보고서를 만듭니다. 동적 쿼리 내에서 세 개의 열과 관련된 CROSS APPLY가 있습니다. 이것은 내 합계가 3 배 이상 커야합니다. 나는 현재 정확한 해답을 얻으려고 카운트를 3으로 나누는 해킹을하고있다. 누구든지이 문제에 대한보다 우아한 해결책을 찾도록 도와 줄 수 있습니까?SQL 피봇 테이블이 집계 결과에 교차 적용합니다.

편집 :

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); 
: 나는 (실제로 도면이다하지만 난 그게 SO의 전체 스키마를 다시하는 것이 현명했다 느끼지 않았다)이 데이터 집합을 감안할 때 SQL 서버 2008R2

을 사용하고 있습니다

그런 다음, 다음과 같은 쿼리를 사용 :

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의 SELECTS 수를 변경하면 총계가 같은 방식으로 변경되기 때문에 CROSS APPLY에서 오는 것이 확실합니다.

나는 이것을 할 수있는 더 좋은 방법이있을 것이라고 확신하지만 실패했습니다. 나는 DISTINCT를 작동시키려는 데 꽤 많은 시간을 보냈지 만 PIVOT 내에서 COUNT DISTINCT가 작동하지 않는 것처럼 보였습니다.

누구든지 내게이 문제에 대해 조언을 해줄 수 있다면 크게 감사하겠습니다.

+0

'COUNT (DISTINCT MitigationActionID)'는 작동합니까? – NickyvV

+0

@NickyvV, 아니요, 나는 그 일을하기 위해 많은 시간을 보냈지 만 해결책을 찾지 못했습니다. 'DISTINCT '키워드 근처에'잘못된 구문이 나타납니다 .' – Matthew

답변

6

문제는 CROSS APPLY를 적용하는 것이므로 필요하지 않습니다. 일반적으로 CROSS APPLY 또는 UNPIVOT을 사용합니다. 여러 열을 피벗해야하지만 한 열만 피벗 팅하면 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 |