我们正在查询RFID数据库,并计算特定每小时时间范围内的读取数量。但是当没有记录时,时间框架不会显示,因为它应该如此。但是,我们希望实际报告在该时间范围内没有记录。
这是查询:
select
date_tr,
right('0000'+ convert(varchar,timeofday),2) + '00 - ' + right('0000'+ convert(varchar,timeofday+1),2) + '00' as timeofday,
count(placa) reads
from
(select distinct gi.placa,
convert(varchar(10), tr.inDate,101) as date_tr,
datepart(hour, tr.inDate) timeofday
from
RFIDTransaction tr, RFIDGeneralInformation gi
where
gi.tag = tr.tag
and inDate between current_timestamp - 1 and current_timestamp
and inLoc = 'GATE IN'
and gi.removed = 0
and tr.removed = 0
group by convert(varchar(10), tr.inDate,101), datepart(hour, tr.inDate), gi.placa) xx
group by date_tr, timeofday
order by date_tr, timeofday
此查询的输出如下:
date_tr timeofday reads
01/19/2014 0500 - 0600 12
01/19/2014 0600 - 0700 15
01/19/2014 0800 - 0900 22
...
现在,我们需要显示时间范围0700 - 0800,并且ZERO读取。
我该怎么做?
非常感谢
答案 0 :(得分:3)
这是一个2005年友好的解决方案。
;WITH t AS
(
SELECT DISTINCT gi.placa, -- not sure that you really want distinct there
d = DATEADD(DAY, DATEDIFF(DAY,0,tr.inDate),0), h = DATEPART(HOUR, tr.inDate)
FROM dbo.RFIDTransaction AS tr
INNER JOIN dbo.RFIDGeneralInformation AS gi
ON tr.tag = gi.tag
WHERE tr.inDate >= DATEADD(DAY, -1, CURRENT_TIMESTAMP)
AND tr.inDate < CURRENT_TIMESTAMP
AND tr.removed = 0 AND gi.removed = 0
AND tr.inLoc = 'GATE IN'
),
h(h) AS
(
SELECT TOP (24) DATEADD(HOUR,-number,DATEADD(HOUR, DATEDIFF(HOUR,0,GETDATE()),0))
FROM master.dbo.spt_values WHERE [type] = N'P' AND number > 0
ORDER BY number
),
s AS
(
SELECT
dh = h.h,
d = CONVERT(CHAR(10), h.h, 101),
hs = CONVERT(CHAR(2), h.h, 108),
he = CONVERT(CHAR(2), DATEADD(HOUR, 1, h.h), 108),
c = COUNT(t.d)
FROM h LEFT OUTER JOIN t
ON DATEADD(HOUR, t.h, t.d) >= h.h
AND DATEADD(HOUR, t.h, t.d) < DATEADD(HOUR, 1, h.h)
GROUP BY h.h
)
SELECT
date_tr = d,
timeofday = RIGHT('0' + hs,2) + '00 - ' + RIGHT('0' + he,2) + '00',
reads = c
FROM s ORDER BY dh;
隐含地解决了各种问题(点击了解更多信息):
VARCHAR
without explicitly declaring length VARCHAR
to strip time和part two BETWEEN
for date range queries 对于后人,原帖如下:
您需要先创建一组所有可能的时间范围,然后再创建外连接。一般技术如下(您可以弄清楚如何将其合并到现有代码中,这远远超出了实际问题的范围IMHO)。
在SQL Server 2008中,您可以执行以下操作:
DECLARE @d TABLE(d DATETIME);
INSERT @d SELECT '05:03' UNION ALL SELECT '05:07' UNION ALL SELECT '07:05';
;WITH x(s,e,i) AS
(
SELECT
s = CONVERT(TIME, DATEADD(HOUR, number, 0)),
e = CONVERT(TIME, DATEADD(HOUR, number+1, 0)),
i = RIGHT('0' + RTRIM(number),2) + '00 - '
+ RIGHT('0' + RTRIM(number+1),2) + '00'
FROM
(
SELECT TOP (24) number
FROM master.dbo.spt_values
WHERE [type] = N'P'
ORDER BY number
)
AS x
)
SELECT x.i, COUNT(d.d) FROM x
LEFT OUTER JOIN @d AS d
ON CONVERT(TIME, d.d) >= x.s
AND CONVERT(TIME, d.d) < x.e
-- AND date is the date you're looking for
GROUP BY x.i;
2005年要做的工作要少得多,因为你无法利用TIME
。当我有更多时间时,我将不得不重新审视这个,只是想表达让你开始的基本方法。
答案 1 :(得分:0)
最简单的方法是创建另一个预先填充了所有24个时间段的表,并使用它进行连接:
Interval_ID Interval
1 0000-0100
2 0100-0200
...
执行外连接,任何没有数据的间隔都会在'reads'中显示为null。如果需要,可将它们转换为零。
对于日期而言,这是一种相当常见的技术,并且没有理由不能用间隔来完成。有时候,一个表只包含1到1000之间的所有整数列表,这样就更方便了。只是为了解决这种计数问题。
-update -
如果无法创建要加入的表,则可以尝试加入查询中创建的集。语法如下:
select
intervals.interval,
mytable.*
from
mytable
right join (
select '0000-0100' as interval
union all
select '0100-0200' as interval
union all
...
select '2300-2400' as interval ) intervals
on
mytable.interval = intervals.interval
这是我头顶的代码,但它应该让你指向正确的方向。不优雅,但由于你只关注24个值,所以它并不太糟糕。