我们正在构建一个查询来计算每天每小时的事件数。大多数日子都有几个小时没有任何活动,因此在运行查询的地方,每小时的活动数量会显示,但是存在差距,查询会排除这些。我们仍然希望显示没有活动的小时数并显示零,以便可以绘制零值。我们使用的查询看起来像这样......
select datepart(Year, dev_time) as Year,
datepart(Month, dev_time) as Month,
datepart(Day, dev_time) as Day,
datepart(Hour, dev_time) as Hour,
count(tdm_msg) as Total_ACTIVITES
from TCKT_ACT
where tdm_msg = ‘4162′ and dev_time >= DATEADD(day, - 1, GETDATE())
group by datepart(Year, dev_time) ,
datepart(Month, dev_time) ,
datepart(Day, dev_time),
datepart(Hour, dev_time)
order by datepart(Year, dev_time) asc,
datepart(Month, dev_time) asc,
datepart(Day, dev_time) asc,
datepart(Hour, dev_time) asc
答案 0 :(得分:3)
您将以某种方式需要一个包含日期和时间的表,然后您必须在该表与查询之间进行外部联接。这是我将如何做到这一点。请注意,此解决方案仅适用于SQL Server 2005和2008.如果您没有这些平台,则必须在数据库中实际创建一个时间表,您可以从中加入:
DECLARE @MinDate DATETIME;
SET @MinDate = CONVERT(varchar, GETDATE(), 101);
WITH times AS (
SELECT @MinDate as dt, 1 as depth
UNION ALL
SELECT DATEADD(hh, depth, @MinDate), 1 + depth as depth
FROM times
WHERE DATEADD(hh, depth, @MinDate) <= GETDATE())
SELECT DATEPART(YEAR, t.dt) as [Year],
DATEPART(MONTH, t.dt) as [Month],
DATEPART(DAY, t.dt) as [Day],
DATEPART(HOUR, t.dt) as [Hour],
COUNT(tdm_msg) as Total_ACTIVITES
FROM times t
LEFT JOIN (SELECT * FROM TCKT_ACT WHERE tdm_msg = '4162' and dev_time >= @MinDate) a
ON DATEPART(HOUR, t.dt) = DATEPART(HOUR, a.dev_time)
AND MONTH(t.dt) = MONTH(a.dev_time)
AND DAY(t.dt) = DAY(a.dev_time)
AND YEAR(t.dt) = YEAR(a.dev_time)
GROUP BY DATEPART(YEAR, t.dt) ,
DATEPART(MONTH, t.dt) ,
DATEPART(DAY, t.dt),
DATEPART(HOUR, t.dt)
ORDER BY DATEPART(YEAR, t.dt) asc,
DATEPART(MONTH, t.dt) asc,
DATEPART(DAY, t.dt) asc,
DATEPART(HOUR, t.dt) asc
OPTION (MAXRECURSION 0); /* Just in case you want a longer timespan later on... */
请注意,顶部的WITH语句称为递归公用表表达式,是生成具有相对较少元素的连续表的好方法,就像您在此处一样。
答案 1 :(得分:3)
首先,我根据描述的递归公用表查询创建了一个表函数 戴夫马克尔(谢谢你给我看这个戴夫!)。这非常甜蜜,因为我只需要做一次这个功能,我可以用它来分析任何间隔。
if exists (select * from dbo.sysobjects where name = 'fn_daterange') drop function fn_daterange;
go
create function fn_daterange
(
@MinDate as datetime,
@MaxDate as datetime,
@intval as datetime
)
returns table
--**************************************************************************
-- Procedure: fn_daterange()
-- Author: Ron Savage
-- Date: 12/16/2008
--
-- Description:
-- This function takes a starting and ending date and an interval, then
-- returns a table of all the dates in that range at the specified interval.
--
-- Change History:
-- Date Init. Description
-- 12/16/2008 RS Created.
-- **************************************************************************
as
return
WITH times (startdate, enddate, intervl) AS
(
SELECT @MinDate as startdate, @MinDate + @intval - .0000001 as enddate, @intval as intervl
UNION ALL
SELECT startdate + intervl as startdate, enddate + intervl as enddate, intervl as intervl
FROM times
WHERE startdate + intervl <= @MaxDate
)
select startdate, enddate from times;
go
因此,如果你自己选择该函数,你会得到一个如下所示的时间间隔表:
fn_daterange('12 / 14/2008 10:00:00','12 / 14/2008 20:00:00','01:00:00')
返回:
startdate enddate intervl
----------------------- ----------------------- -----------------------
2008-12-14 10:00:00.000 2008-12-14 10:59:59.997 1900-01-01 01:00:00.000
2008-12-14 11:00:00.000 2008-12-14 11:59:59.997 1900-01-01 01:00:00.000
2008-12-14 12:00:00.000 2008-12-14 12:59:59.997 1900-01-01 01:00:00.000
2008-12-14 13:00:00.000 2008-12-14 13:59:59.997 1900-01-01 01:00:00.000
2008-12-14 14:00:00.000 2008-12-14 14:59:59.997 1900-01-01 01:00:00.000
2008-12-14 15:00:00.000 2008-12-14 15:59:59.997 1900-01-01 01:00:00.000
2008-12-14 16:00:00.000 2008-12-14 16:59:59.997 1900-01-01 01:00:00.000
2008-12-14 17:00:00.000 2008-12-14 17:59:59.997 1900-01-01 01:00:00.000
2008-12-14 18:00:00.000 2008-12-14 18:59:59.997 1900-01-01 01:00:00.000
2008-12-14 19:00:00.000 2008-12-14 19:59:59.997 1900-01-01 01:00:00.000
2008-12-14 20:00:00.000 2008-12-14 20:59:59.997 1900-01-01 01:00:00.000
然后我制作了一个事件数据样本表:
eventdate eventnote
----------------------- --------------------
2008-12-14 10:01:00.000 oo! an event!
2008-12-14 10:01:00.000 oo! an event!
2008-12-14 10:01:00.000 oo! an event!
2008-12-14 10:01:00.000 oo! an event!
2008-12-14 10:23:00.000 oo! an event!
2008-12-14 10:23:00.000 oo! an event!
2008-12-14 10:23:00.000 oo! an event!
2008-12-14 11:23:00.000 oo! an event!
2008-12-14 11:23:00.000 oo! an event!
2008-12-14 11:23:00.000 oo! an event!
2008-12-14 11:23:00.000 oo! an event!
2008-12-14 11:23:00.000 oo! an event!
2008-12-14 14:23:00.000 oo! an event!
2008-12-14 14:23:00.000 oo! an event!
2008-12-14 14:23:00.000 oo! an event!
2008-12-14 19:23:00.000 oo! an event!
2008-12-14 19:23:00.000 oo! an event!
2008-12-14 19:23:00.000 oo! an event!
2008-12-14 19:23:00.000 oo! an event!
2008-12-14 19:00:00.000 oo! an event!
2008-12-14 19:00:00.000 oo! an event!
2008-12-14 19:00:00.000 oo! an event!
22 Row(s) affected
然后我把它们连在一起,左边是一个LETER OUTER JOIN:
select
dr.startdate,
dr.enddate,
count(me.eventdate) as eventcount
from
fn_daterange('12/14/2008 10:00:00', '12/14/2008 20:00:00', '01:00:00' ) dr
LEFT OUTER JOIN myevents me
on ( me.eventdate between dr.startdate and dr.enddate)
group by
dr.startdate,
dr.enddate
startdate enddate eventcount
----------------------- ----------------------- ----------
2008-12-14 10:00:00.000 2008-12-14 10:59:59.993 7
2008-12-14 11:00:00.000 2008-12-14 11:59:59.993 5
2008-12-14 12:00:00.000 2008-12-14 12:59:59.993 0
2008-12-14 13:00:00.000 2008-12-14 13:59:59.993 0
2008-12-14 14:00:00.000 2008-12-14 14:59:59.993 3
2008-12-14 15:00:00.000 2008-12-14 15:59:59.993 0
2008-12-14 16:00:00.000 2008-12-14 16:59:59.993 0
2008-12-14 17:00:00.000 2008-12-14 17:59:59.993 0
2008-12-14 18:00:00.000 2008-12-14 18:59:59.993 0
2008-12-14 19:00:00.000 2008-12-14 19:59:59.993 7
2008-12-14 20:00:00.000 2008-12-14 20:59:59.993 0
11 Row(s) affected
甜蜜的HOLY CRAP - 我可以在工作中使用它进行各种分析! : - )
感谢Fred提出的问题和Dave关于常见表查询的信息!
罗恩
答案 2 :(得分:2)
我们在某些性能监控软件上遇到了类似的问题但是,在DB2 / z大型机商店中,我们已经不再需要做SQL体操来获得这些结果。在他们检索的每一行上执行“函数”的SQL查询都是众所周知的不可扩展的,如果我们尝试使用它们,那么DBA就会在场上嘲笑我们。
相反,我们发现重构数据库模式更容易包含每行中的事件计数(显然我们的DBA不介意使用更多的磁盘空间,只是更多的CPU咕噜声)。在您的情况下,这将添加一个名为tdm_quant
的列,您为插入的每一行(即每个事件)设置为1。
然后,您的查询的第五个字段从count(tdm_msg)
更改为sum(tdm_quant)
,这将获得相同的结果。
除此之外,您可以在tdm_quant
字段中插入特殊记录(每小时一次,或者每天开始时24次,或者如果您愿意,可以填写1月1日的整年)设置为零。归零时,这些记录对sum(tdm_quant)
没有任何影响,但您会得到所需的行为,当天每小时返回一行,其中Total_ACTIVITIES
为零在那个小时内没有发生任何事件。
您的查询的其余部分无需更改。
答案 3 :(得分:0)
听起来你可以使用另一个数字为1到24的表来使用“左外连接”......
答案 4 :(得分:0)
这里的基本答案涉及左外连接(LOJ)和显式COUNT(column)
,因为它不计算空值,但COUNT(*)计算所有行。困难的部分是生成一个表来对抗LOJ。 WITH子句和递归解决方案将在许多DBMS中工作(显然,MS SQL Server,几乎可以肯定是DB2 - 也可能是其他的)。
许多DBMS支持临时表和存储过程;组合可用于使用适当的日期/时间字段值填充表,然后针对该表执行LOJ(或者更确切地说,FROM temp_table LEFT OUTER JOIN main_table ...)。不是整洁,但适用于大多数地方。