从now()函数中减去小时数

Cha*_*ti 4 sql postgresql timezone datetime date-arithmetic

我们有一台24x7全天候运行的机器.每天我都会报告每小时生产的件数.在我们的例子中,一个工作日意味着'2015-06-16 06:00:00'到'2015-06-17 06:00:00'.

这是我的代码:

select date_trunc('hour', t_el_eventlog.eventtime at time zone 'CET') as hours,
       count (distinct t_el_eventlog.serialnumber) as count
from t_el_eventlog
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
                                       and '2015-06-17 06:00:00'
and sourceid = '44'
group by hours
order by hours asc
Run Code Online (Sandbox Code Playgroud)
  • 我的Postgres版本:"PostgreSQL 9.4.1,由Visual C++编译构建1800,32位"

  • 我正在处理的两列数据类型:

    eventtime timestamp without time zone
    sourceid  integer NOT NULL
    
    Run Code Online (Sandbox Code Playgroud)
  • 时区是"欧洲/柏林".

通过上面的查询,我得到了我想要的信息,但我必须每天更改日期.是否可以将该now()功能用作我的情况的默认值,这样我就不必每天手动更改日期?

Gor*_*off 6

您可以使用CURRENT_DATE

 select date_trunc('hour', t_el_eventlog.eventtime at time zone 'CET') as hours,
        count(distinct t_el_eventlog.serialnumber) as count
 from t_el_eventlog
 where eventtime at time zone 'CET' between CURRENT_DATE + interval '6 hour' and
                                            CURRENT_DATE + interval '30 hour' and
       sourceid = '44'
 group by hours
 order by hours asc;
Run Code Online (Sandbox Code Playgroud)

编辑:

埃尔文的评论是关于问题而不是这个答案。用于between日期/时间是一个坏主意。我想在每个这样做的问题中都应该重复这一点。但问题是作为日期之间边界的日期/时间值被计算两次。

正确的逻辑是:

 select date_trunc('hour', t_el_eventlog.eventtime at time zone 'CET') as hours,
        count(distinct t_el_eventlog.serialnumber) as count
 from t_el_eventlog
 where eventtime at time zone 'CET' >= CURRENT_DATE + interval '6 hour' and
       eventtime at time zone 'CET' < CURRENT_DATE + interval '30 hour' and
       sourceid = '44'
 group by hours
 order by hours asc;
Run Code Online (Sandbox Code Playgroud)

请注意第二个限制的“<”。 是关于这个主题的一个很好的博客。尽管 Aaron 专注于 SQL Server,但警告(以及一些解决方案)也适用于其他数据库。


Erw*_*ter 6

回答 timestamp

你需要了解的数据类型的性质timestamp without time zonetimestamp with time zone(名字可以欺骗).如果你不这样做,请先阅读:

AT TIME ZONE结构将您timestamptimestamptz,这几乎可以肯定是错误的举动:

where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
                                       and '2015-06-17 06:00:00'
Run Code Online (Sandbox Code Playgroud)

首先,它会杀死性能.应用AT TIME ZONEeventtime使得表达优化搜索.Postgres不能使用普通索引eventtime.但即使没有索引,sargable表达式也会更便宜.提供根据表中的值调整的边界,这样您就不必操纵每一行.
可以使用匹配的表达式索引进行补偿,但无论如何它可能只是一个误解和错误.

那个表达会发生什么?

  1. AT TIME ZONE 'CET'通过附加当前时区的时区偏移量来转换timestamp值.这会将DST(夏令时)考虑在内,因此您可以获得冬季时间戳的不同偏移量.基本上你得到了问题的答案:eventtimetimestamptz

    当给定时区看到给定时间戳时,绝对时间(UTC时间戳)是多少?

    当向用户显示结果时,它将成为会话当前时区的相应本地时间戳,并附加相应的时区偏移量.(可能与表达式中使用的相同或不同).

  2. 右侧的字符串文字没有数据类型,因此它们的类型是从表达式中的赋值派生的.因为我们timestamptz现在有效地拥有,所以两者都被投射到timestamptz,假定当前的会话时区.

    当地时间看起来像给定的时间戳时,给我当时的UTC时间戳.

    偏移量随DST规则而变化.

简而言之,如果你在任何地方都使用相同的时区:CET或者'Europe/Berlin',对于现在的时间戳,同样的事情,但不是历史的或(可能的)未来的,你可以减少残余.

表达式的第二个问题:BETWEEN值几乎总是错误的timestamp.细节:

SELECT date_trunc('hour', eventtime) AS hour
     , count(DISTINCT serialnumber)  AS ct  -- sure you need distinct?
FROM   t_el_eventlog
WHERE  eventtime >= now()::date - interval '18 hours'
AND    eventtime <  now()::date + interval '6 hours'
AND    sourceid  =  44  -- don't quote the numeric literal
GROUP  BY 1
ORDER  BY 1;
Run Code Online (Sandbox Code Playgroud)

now()是Postgres实现的SQL标准CURRENT_TIMESTAMP.两者都返回timestamptz(不timestamp!).你可以使用其中之一.
now()::date相当于CURRENT_DATE.两者都取决于当前时区设置.

你应该有一个表格的索引:

CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Run Code Online (Sandbox Code Playgroud)

或者,允许仅索引扫描:

CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Run Code Online (Sandbox Code Playgroud)

如果你在不同的时区操作,事情变得更复杂,你应该timestamptz用于一切.

替代 timestamptz

在问题更新之前,似乎时区很重要.处理不同时区时,"今天"是当前时区的功能依赖.人们往往会忘记这一点.

要仅使用会话的当前时区设置,请使用与上面相同的查询.如果在不同的时区执行,结果实际上是错误的.(也适用于上述内容.)

为了保证给定时区(在您的情况下为"Europe/Berlin")的正确结果,无论会话的当前时区设置如何,请使用以下表达式:

    ((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
            AT TIME ZONE 'Europe/Berlin'  -- 2nd time to convert back
Run Code Online (Sandbox Code Playgroud)

请注意,AT TIME ZONE构建返回timestamptimestamptz输入,反之亦然.

正如一开始所提到的,这里所有的血腥细节: