不考虑时间范围内的记录

时间:2014-01-20 16:46:17

标签: sql sql-server sql-server-2005

我们正在查询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读取。

我该怎么做?

非常感谢

2 个答案:

答案 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;

隐含地解决了各种问题(点击了解更多信息):

对于后人,原帖如下:


您需要先创建一组所有可能的时间范围,然后再创建外连接。一般技术如下(您可以弄清楚如何将其合并到现有代码中,这远远超出了实际问题的范围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个值,所以它并不太糟糕。