【问题标题】:Query fails when start-time of timestamp is bigger than end-time of timestamp当时间戳的开始时间大于时间戳的结束时间时查询失败
【发布时间】:2012-08-15 10:02:30
【问题描述】:

我有一个基于 CTE 的查询,我在其中检索两个给定时间跨度之间的每小时间隔。我的查询如下:

获取开始和结束日期时间(例如 07-13-2011 00:21:09 和 07-31-2011 21:11:21)获取每小时间隔之间的每小时总查询值(这里是从 00 到21,总共 21 小时,但这是参数化的,取决于我每天为输入提供的小时数。

此查询适用于第一个时间戳的小时小于第二个时间戳的输入 - 例如。第一个时间戳的小时为 03 AM,第二个时间戳的小时为 07 AM,但存在问题。当我想检索 07-13-2011 22:11:43 和 07-25-2011 04:06:04 等输入的查询总数时,我遇到了问题。我需要检索如下查询的总数:

07-13-2011 22:00:00    143 //representing the total amounts of queries 22:11:43 - 22:59:59 interval-
07-13-2011 23:00:00    121 //representing the total amounts of queries in 23:00:00  -23:59:59 interval-
07-14-2011 00:00:00     65 //00:00:00  - 00:59:59 interval
07-14-2011 01:00:00     51 //01:00:00  - 01:59:59 interval...
.
.
.
07-14-2011 04:00:00  22  //query amount for 04:00:00 - 04:06:04 interval

等等。除了我在下面写的 CTE 查询,我还需要做什么?

WITH cal AS (
    SELECT generate_series('2011-02-02 00:00:00'::timestamp
                         , '2012-04-01 05:00:00'::timestamp
                         , '1 hour'::interval) AS stamp
    )
, qqq AS (
    SELECT date_trunc('hour', calltime) AS stamp
    , count(*) AS zcount
    FROM mytable
    WHERE calltime >= '07-13-2011 22:00:00'
    AND calltime <='07-31-2011 04:33:21' 
    AND calltime::time >= '22:00:00' 
    AND calltime::time <= '04:33:21'
    -- this calltime::time part obviously doesn't work due to common sense and logic
    -- edited it to show what I try to mean 
    AND date_part('hour', calltime) >= 0
    AND date_part('hour', calltime) <= 21
    GROUP BY date_trunc('hour', calltime)
    )
SELECT cal.stamp
     , COALESCE (qqq.zcount, 0) AS zcount
FROM cal
LEFT JOIN qqq ON cal.stamp = qqq.stamp
WHERE cal.stamp >= '07-13-2011 22:00:00'
AND cal.stamp<='07-31-2011 04:33:21'
AND date_part('hour', cal.stamp) >= 0
AND date_part('hour', cal.stamp) <= 21
ORDER BY stamp ASC;

【问题讨论】:

  • 文本和示例在几个细节上相互矛盾(例如:您是否要将结果中的小时数限制为0 - 21?)。我为我的答案选择了一个解释并添加了可选的注释子句。
  • @ErwinBrandstetter 是的,我应该解决这个问题。我想将小时数限制为用户作为输入提供的时间间隔 - 这是在通话时间和 cal.stamp ... 之间给出的时间戳,在我的示例中是一天 22:00:00 和另一天的 04:32:21,该间隔内的所有天数(包括输入天数)将显示 22 到 04:32 等之间的每小时查询计数。
  • 那还是模棱两可:all the days within that interval (including the input days) will show the hourly query counts between 22 and 04:32 etc.- parts. 尽量说清楚!您是否真的想将第一天的 00:00 - 04:32 也包含在内?
  • @ErwinBrandstetter 如果您的意思是是否应包括 2011 年 7 月 13 日 00:00 - 04:32 时间间隔,答案是否定的。它应该从 2011 年 7 月 13 日 22:00 开始,然后从那里开始,在 2011 年 7 月 31 日 04:32 停止。

标签: sql postgresql datetime aggregate-functions common-table-expression


【解决方案1】:

考虑这个修改后的版本:

WITH param AS (
   SELECT '2011-07-13 22:11:43'::timestamp AS start -- supply start / stop once
         ,'2011-07-25 04:06:04'::timestamp AS stop
   )
   , cal   AS (
   SELECT generate_series(date_trunc('hour', p.start)
                         ,date_trunc('hour', p.stop + interval '1h')
                         ,interval '1h') AS h
   FROM param p
   )
   , q    AS (
   SELECT date_trunc('hour', calltime) AS h
         ,count(*) AS ct
   FROM   mytable
         ,param p
   WHERE  calltime >= p.start
   AND    calltime <= p.stop
   -- uncomment if you actually want to exclude hours 22 & 23 (?)
   -- AND    extract('hour' FROM calltime) BETWEEN 0 AND 21
   GROUP  BY 1
   )
SELECT cal.h, COALESCE(q.ct, 0) AS ct
FROM   cal
LEFT   JOIN q USING (h)
-- uncomment if you actually want to exclude hours 22 & 23 (?)
-- WHERE  extract('hour' FROM cal.h) BETWEEN 0 AND 21
ORDER  BY 1;

主要变化是立即从实际时间跨度生成小时。
删除了几个不需要的条件。
对时间戳使用 ISO 8601 格式(适用于所有语言环境)。

在此related answer 查找更多上下文和链接。唯一的区别:那里是关于 running 的计数。

【讨论】:

    【解决方案2】:

    这个子句:

    AND calltime::time >= '00:21:09' AND calltime::time <= '21:11:21'
    

    考虑到其他条款,看起来很可疑。

    尚不清楚为什么需要它,如果作为示例给出的文字时间分别为 22:11:4304:06:04 不起作用,则此子句将过滤掉所有内容,因此您最终会得到 0 个计数每小时。

    【讨论】:

      猜你喜欢
      • 2012-09-04
      • 2021-01-26
      • 1970-01-01
      • 2012-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多