我正在使用带有动态创建列的数据透视表来创建一个按月计算操作的报表。在动态查询中是一个涉及三列的CROSS APPLY。这导致我的总数增加了三倍。我目前正在攻击一个解决方案,我将计数除以3得到正确的答案。任何人都可以帮我提出一个更优雅的解决方案吗?
编辑:我正在使用SQL Server 2008R2
鉴于此数据集(它实际上是一个视图,但我觉得在SO中重新创建整个模式并不明智):
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 |
如果我在上面的查询中的注释中没有包含我的hack,则输出表中的所有1都变为3。我很确定它来自CROSS APPLY,因为如果我在CROSS APPLY中更改SELECTS的数量,则总数会以相同的方式改变。
我确信有更好的方法可以做到这一点,但我没有成功。我花了很多时间试图让DISTINCT工作,但我似乎无法从PIVOT中获得COUNT DISTINCT。
如果有人能就此提出任何建议,我将不胜感激。
答案 0 :(得分: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代码将是:
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 |