将一个月内的日期作为列进行调整;可能没有动态的SQL?

时间:2011-12-14 22:55:26

标签: sql sql-server-2008 tsql

所以我有一个包含这样的时钟信息的SQL表。员工在特定日期工作的分钟数。该表持有数年但却缺少星期六和星期日以及其他非工作日/假期(偶尔例外)。

 EmployeeID   Date Attended   Total Minutes
 -----------------------------------
 001          '11/01/2011'    319
 002          '11/01/2011'    355
 003          '11/01/2011'    352
 001          '11/02/2011'    340
 002          '11/02/2011'    322
 003          '11/02/2011'    351

我需要编写一个数据透视表,在报告中显示这样的数据,汇总给定月份的出勤率。

 EmployeeId  '11/01/2011'  '11/02/2011' ....  (for all dates in a month)
 -----------------------------------------------------------------------
 001         319           340
 002         355           322
 003         352           351

我有一个有效的解决方案(见下文)...... 但我的方法使用动态sql 从给定月份的日期构建数据透视表。在我的脑海里,我一直认为必须有一种不使用动态SQL的方法 - 但它并没有找到我。任何建议...... 我坚持使用动态SQL吗?

DECLARE @columns VARCHAR(8000)
DECLARE @headers VARCHAR(8000)
DECLARE @lowdate Date 
DECLARE @highdate Date

SELECT @columns = COALESCE(@columns + ',[' + cast(Attended as varchar) + ']',
                     '[' + cast(Attended as varchar)+ ']')
FROM TimeAttended WHERE attended >= @date and attended <= @highdate
GROUP BY Attended

SELECT @headers = COALESCE(@Headers + ',Sum([' + cast(Attended as varchar) + ']) as [' + cast(Attended as varchar) + ']',
                     'Sum([' + cast(Attended as varchar)+ ']) as [' + cast(Attended as varchar) + ']')
FROM TimeAttended WHERE attended >= @date and attended <= @highdate
GROUP BY Attended

DECLARE @query VARCHAR(8000)
SET @query = '
SELECT employeeid, ' + @headers + '
FROM TimeAttended 
PIVOT
 (
 Sum(TotalMinutes)
 FOR [Attended]
 IN (' + @columns + ')
 )
 AS p group by employeeid'

EXECUTE(@query)

3 个答案:

答案 0 :(得分:1)

除了动态SQL,
如果每一位输出只属于一个月,那么你可以将所有日期的硬编码从1到31:

with data as (
  select *
  from (
    values
     (001,          '20111101',    319),
     (002,          '20111101',    355),
     (003,          '20111101',    352),
     (001,          '20111102',    340),
     (002,          '20111102',    322),
     (003,          '20111102',    351)
  ) foo (EmployeeID, [Date], [Minutes])
),
prepared_data as (
  select EmployeeID, [Minutes], day([date]) as [day]
  from data
)
select *
from
  prepared_data
  pivot (min([Minutes]) for [day] in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31])) pvt
;

然后在稍后阶段隐藏或忽略仅有null s的列。

答案 1 :(得分:1)

除了显而易见的使用动态SQL(我通常希望尽可能避免)之外,还有两个静态选项。

第一种方法是将行正常运行,然后在显示代码中旋转结果(无论你有多少,都会这样做)。这假设您正在生成某种类型的报告,并且具有必要的处理能力。

第二个要求略微区分数据 不是创建语句来查看特定的日期集,而是编写它以查看过去31天的(从某个给定日期开始)。
也就是说,column2是给定日期,column3是给定日期--1天等等:

SELECT a.Id, COALESCE(b.Minutes, 0), COALESCE(c.Minutes, 0), (etc)
FROM Employees as a
LEFT JOIN TimeClock as b
ON b.EmployeeId = a.id
AND b.Date = @Given
LEFT JOIN TimeClock as c
ON C.EmployeeId = a.Id
AND c.Date = DATEADD(DAY, 1, @Given)
(etc)

...然后将其映射到结果的column1到显示表的column1等等。

答案 2 :(得分:0)

如果您希望列标题基于值,则必须提前知道值,否则必须使用动态SQL。

唯一可以执行此操作的数据库是MS Access'TRANSFORM