SQL使用动态列对同一行进行多次计数

时间:2013-07-12 10:08:08

标签: sql sql-server sql-server-2008 pivot

我需要更改在同一行按周期显示用户数(ScheduleID)的视图。现在,Period表内容可以增长并包含3个以上的句点。

实际的SQL是:

SELECT r.Code,
 SUM(CASE WHEN s.PeriodID=1 THEN 1 ELSE 0 END) AS PeriodID1,
 SUM(CASE WHEN s.PeriodID=2 THEN 1 ELSE 0 END) AS PeriodID2,
 SUM(CASE WHEN s.PeriodID=3 THEN 1 ELSE 0 END) AS PeriodID3,
 SUM(CASE WHEN s.PeriodID IN (1,2,3) THEN 1 ELSE 0 END) AS Total
 FROM Schedules s
 JOIN Periods p ON p.PeriodID = s.PeriodID
 JOIN Resources r ON r.ResourceID = s.ResourceID
 GROUP BY r.Code;

示例数据: 表格表

ScheduleID(int) ResourceID(int) ResourceCode(varchar 4) PeriodID(int)
1               1               AA                      1
2               1               AA                      3
3               1               AA                      3
4               2               BB                      1
5               3               CC                      1
6               1               AA                      1
7               3               CC                      2
8               3               CC                      3
9               2               BB                      1
10              2               BB                      2
11              2               BB                      3
12              1               AA                      3

表期间

PeriodID(int) Code (varchar 4)
1             P1 
2             P2
3             P3
4             P4  
5             P5
6             P6
7             P7
8             P8

我需要的结果是:

ResourceCode PeriodID1 PeriodID2 PeriodID3 ... PeriodID8  TOTAL
AA           2         0         3             0          5
BB           2         1         1             0          4
CC           1         1         1             0          3

“期间”表格内容现在是动态的。

数据库版本是Microsoft SQL 2008

我想知道是否可以在没有创建存储过程的情况下执行此操作...并在一个查询中执行此操作:

SELECT *
FROM (
SELECT R.Code, P.PeriodID, COUNT(S.ScheduleID) AS RPCount
FROM Schedules S INNER JOIN Periods P ON S.PeriodID = P.PeriodID
JOIN Resources R ON S.ResourceID = R.ResourceID
WHERE S.ResourceID is not null
GROUP BY R.Code, P.PeriodID
) as data
PIVOT
(
    SUM(RPCount)
    --FOR PeriodID IN ([1],[2],[3])
    FOR PeriodID IN (SELECT PeriodID From Periods)
)AS pvt
ORDER BY Code

3 个答案:

答案 0 :(得分:4)

由于您使用的是SQL Server,因此可以实现PIVOT功能,如果您有未知数量的句点值,则需要使用动态SQL:

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

select @cols = STUFF((SELECT distinct ',' + QUOTENAME('PeriodId'+cast(periodid as varchar(10))) 
                    from Periods
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT resourcecode, ' + @cols + ' , Total
            from 
            (
               select s.resourcecode, 
                 ''PeriodId''+cast(p.periodid as varchar(10)) period,
                count(*) over(partition by s.resourcecode) Total
               from periods p
               left join schedules s
                 on p.periodid = s.periodid
            ) x
            pivot 
            (
                count(period)
                for period in (' + @cols + ')
            ) p 
            where resourcecode is not null
            order by resourcecode'

execute(@query)

SQL Fiddle with Demo。这给出了一个结果:

| RESOURCECODE | PERIODID1 | PERIODID2 | PERIODID3 | PERIODID4 | PERIODID5 | PERIODID6 | PERIODID7 | PERIODID8 | TOTAL |
------------------------------------------------------------------------------------------------------------------------
|           AA |         2 |         0 |         3 |         0 |         0 |         0 |         0 |         0 |     5 |
|           BB |         2 |         1 |         1 |         0 |         0 |         0 |         0 |         0 |     4 |
|           CC |         1 |         1 |         1 |         0 |         0 |         0 |         0 |         0 |     3 |

根据您之前使用MySQL标记的问题,我假设您使用MySQL作为数据库。如果是这样,那么你没有PIVOT函数,因此你必须使用带有CASE表达式的聚合函数将数据行转换为列。

如果您的列值已知,则可以对查询进行硬编码:

select resourcecode,
  sum(case when period = 'PeriodId1' then 1 else 0 end) PeriodId1,
  sum(case when period = 'PeriodId2' then 1 else 0 end) PeriodId2,
  sum(case when period = 'PeriodId3' then 1 else 0 end) PeriodId3,
  sum(case when period = 'PeriodId4' then 1 else 0 end) PeriodId4,
  sum(case when period = 'PeriodId5' then 1 else 0 end) PeriodId5,
  sum(case when period = 'PeriodId6' then 1 else 0 end) PeriodId6,
  sum(case when period = 'PeriodId7' then 1 else 0 end) PeriodId7,
  sum(case when period = 'PeriodId8' then 1 else 0 end) PeriodId8,
  count(*) Total
from
(
  select concat('PeriodId', p.periodid) Period,
    s.resourcecode
  from periods p
  left join schedules s
    on p.periodid = s.periodid
) d
where resourcecode is not null
group by resourcecode;

SQL Fiddle with Demo。但是如果值是未知的或动态的,那么您将需要使用预准备语句来生成要执行的sql字符串:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'sum(CASE WHEN period = ''',
      concat('PeriodId', periodid),
      ''' THEN 1 else 0 END) AS `',
      concat('PeriodId', periodid), '`'
    )
  ) INTO @sql
FROM periods;

SET @sql 
  = CONCAT('SELECT resourcecode, ', @sql, ' , count(*) Total
            from
            (
              select concat(''PeriodId'', p.periodid) Period,
                s.resourcecode
              from periods p
              left join schedules s
                on p.periodid = s.periodid
            ) d
            where resourcecode is not null
            group by resourcecode');


PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SQL Fiddle with Demo

答案 1 :(得分:0)

使用PIVOT

试试这个

SELECT *
    FROM (
        SELECT 
           S.ResourceCode ,
           P.PeriodID  AS period,
          COUNT(*) AS PCount
        FROM Schedules S INNER JOIN Periods P ON S.PeriodID =P.PeriodID 
        GROUP BY S.ResourceCode ,P.PeriodID
    ) as s
    PIVOT
    (

        PCount,
        FOR [period] IN (SELECT DISTINCT PeriodID From Periods)
    )AS pivot

答案 2 :(得分:0)

请尝试下面的MS Sql server代码:

DECLARE @column VARCHAR(MAX), @SumQuery VARCHAR(MAX)

SELECT 
    @column = COALESCE(@column + '], [', '')+ CAST(PeriodID as nvarchar(10)),
    @SumQuery = COALESCE(@SumQuery + ']+[', '')+ CAST(PeriodID as nvarchar(10))
FROM 
    Periods 
GROUP BY PeriodID

EXEC ('select *, ['+@SumQuery+'] as [Total] From
(
    select * From Schedules
)up
pivot (count(ScheduleID) for PeriodID in (['+@column+'])) as pvt')