我们的系统中有事件,包括开始时间和完成时间以及项目名称(和其他信息)。 我们想要报告:每个项目每月有多少事件具有“开放”状态。 开放状态意味着:未完成。
如果事件发生在2009年12月并于2010年3月结束,那么它应该包括在2009年12月,2010年1月和2月。
需要的结构应该是这样的:
Project Year Month Count
------- ------ ------- -------
Test 2009 December 2
Test 2010 January 10
Test 2010 February 12
....
答案 0 :(得分:0)
这里一个有用的技术是创建一个“所有”日期的表(显然是无限的,所以我的意思是一个足够大的范围用于你的目的)或创建两个表:所有月份中的一个(12行)和另一个“所有”年。
我们假设你选择了第一个:
create table all_dates (d date)
并根据需要填充。我将按如下方式定义您的事件表
create table incident
(
incident_id int not null,
project_id int not null,
start_date date not null,
end_date date null
)
我不确定您使用的RDBMS和日期函数之间的差异很大,因此下一位可能需要根据您的需求进行调整。
select
project_id,
datepart(yy, all_dates.d) as "year",
datepart(mm, all_dates.d) as "month",
count(*) as "count"
from
incident,
all_dates
where
incident.start_date <= all_dates.d and
(incident.end_date >= all_dates.d or incident.end_date is null)
group by
project_id,
datepart(yy, all_dates.d) year,
datepart(mm, all_dates.d) month
这不会像我们想要的那样完全奏效,因为事件在每个月开放的每一天都会有计数。要解决这个问题,我们需要使用子查询或临时表,这实际上取决于RDBMS ......
另一个问题是,对于开放事件,它会在all_dates表中显示所有未来几个月。添加all_dates.d <= today
解决了这个问题。同样,不同的RDBMS有不同的现在/今天/系统时间回馈的方法......
另一种方法是让all_months而不是all_dates表只包含该月中第一个月的日期:
create table all_months (first_of_month date)
select
project_id,
datepart(yy, all_months.first_of_month) as "year",
datepart(mm, all_months.first_of_month) as "month",
count(*) as "count"
from
incident,
all_months
where
incident.start_date <= dateadd(day, -1, dateadd(month, 1, first_of_month)
(incident.end_date >= first_of_month or incident.end_date is null)
group by
project_id,
datepart(yy, all_months.first_of_month),
datepart(mm, all_months.first_of_month)
答案 1 :(得分:0)
在SQL Server中:
SELECT
Project,
Year = YEAR(TimeWhenStillOpen),
Month = DATENAME(month, MONTH(TimeWhenStillOpen)),
Count = COUNT(*)
FROM (
SELECT
i.Project,
i.Incident,
TimeWhenStillOpen = DATEADD(month, v.number, i.StartTime)
FROM (
SELECT
Project,
Incident,
StartTime,
FinishTime = ISNULL(FinishTime, GETDATE()),
MonthDiff = DATEDIFF(month, StartTime, ISNULL(FinishTime, GETDATE()))
FROM Incidents
) i
INNER JOIN master..spt_values v ON v.type = 'P'
AND v.number BETWEEN 0 AND MonthDiff - 1
) s
GROUP BY Project, YEAR(TimeWhenStillOpen), MONTH(TimeWhenStillOpen)
ORDER BY Project, YEAR(TimeWhenStillOpen), MONTH(TimeWhenStillOpen)
简单地说,它是如何运作的:
最直接在“事件”表上工作的内部子选择,只是对表进行“规范化”(将NULL完成时间替换为当前时间)并添加月份差异列MonthDiff
。如果您的案例中没有NULL,请相应删除ISNULL
表达式。
外部子选择使用MonthDiff
将时间范围分解为一系列时间戳,这些时间戳对应于事件仍处于打开状态的月份,即不包括FinishTime月份。名为master..spt_values
的系统表也作为现成的numbers table使用。
最后,主选择只剩下分组数据的任务。