-2

我需要更改在同一行上按时间段显示用户计数(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

Periods 表内容现在是动态的。

数据库版本是 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
4

3 回答 3

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

于 2013-07-12T12:13:38.227 回答
0

使用枢轴

尝试这个

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
于 2013-07-12T10:18:53.807 回答
0

请尝试下面的 MS Sql 服务器代码:

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')
于 2013-07-12T11:46:22.320 回答