Postgres:两个时间戳之间的差异(小时:分钟:秒)

时间:2017-06-23 16:43:25

标签: postgresql date-difference

我创建一个计算两个时间戳之间差异的选择

这里是代码:(您不必理解下面的表格。只需按照主题进行操作)

  (select value from demo.data where id=q.id and key='timestampend')::timestamp 
- (select value from demo.data where id=q.id and key='timestampstart')::timestamp) as durata

看看这个例子,如果你想要更容易:

select timestamp_end::timestamp - timestamp_start as duration

这里的结果是:

1] //" durata"是持续时间

问题是第一个时间戳是2017-06-21,第二个时间是2017-06-22所以我们有1天和几个小时的差异。 我怎么做才能显示结果不像" 1天02:06:41.993657"但是" 26:06:41.993657"没有毫秒(26:06:41)?

更新 我正在测试此查询:

select id as ticketid,
(select value from demo.data where id=q.id and key = 'timestampstart')::timestamp as TEnd,
(select value from demo.data where id=q.id and key = 'timestampend')::timestamp as TStart,
(select
make_interval
(
0,0,0,0, -- years, months, weeks, days
extract(days from duration1)::int * 24 + extract(hours from duration1)::int, -- calculated hours (days * 24 + hours)
extract(mins from duration1)::int, -- minutes
floor(extract(secs from duration1))::int -- seconds, without miliseconds, thus FLOOR()
) as duration1
from
(
(select value from demo.data where id=q.id and key='timestampstart')::timestamp - (select value from demo.data where id=q.id and key='timestampend')::timestamp
) t(duration) as dur
from (select distinct id from demo.data) q

错误是相同的: [错误]错误:语法错误在或附近" ::" id = q.id

时出错

数据表是这样的:

enter image description here

2 个答案:

答案 0 :(得分:1)

在Postgres中,虽然区间数据类型允许小时值大于23(参见https://www.postgresql.org/docs/9.6/static/functions-formatting.html),但to_char()函数将减少天数,如果将delta值设置为“一天内”,则只需“一小时”它并尝试获得'HH24'的价值。

所以,我最终得到了这样的技巧,将to_char(...)与extract(来自......的'epoch')相结合,然后将concatinated值放到另一个to_char():

with timestamps(ts1, ts2) as (
  select
    '2017-06-21'::timestamptz,
    '2017-06-22 01:03:05.1212'::timestamptz
), res as (
  select
    round(extract('epoch' from ts2 - ts1) / 3600) as hours,
    to_char(ts2 - ts1, 'MI:SS') as min_sec
  from timestamps
)
select hours, min_sec, to_char(format('%s:%s', hours, min_sec)::interval, 'HH24:MI:SS')
from res;

结果是:

 hours | min_sec | to_char
-------+---------+----------
    25 | 03:05   | 25:03:05
(1 row)

您可以定义一个SQL函数,以便更轻松地使用它:

create or replace function extract_hhmmss(timestamptz, timestamptz) returns interval as $$
  with delta(i) as (
    select
      case when $2 > $1 then $2 - $1
      else $1 - $2
      end
  ), res as (
    select
      round(extract('epoch' from i) / 3600) as hours,
      to_char(i, 'MI:SS') as min_sec
    from delta
  )
  select
    (
      case when $2 < $1 then '-' else '' end
      || to_char(format('%s:%s', hours, min_sec)::interval, 'HH24:MI:SS')
    )::interval
  from res;
$$ language sql stable;

使用示例:

[local]:5432 nikolay@test=# select extract_hhmmss('2017-06-21'::timestamptz, '2017-06-22 01:03:05.1212'::timestamptz);
 extract_hhmmss
----------------
 25:03:05
(1 row)

Time: 0.882 ms
[local]:5432 nikolay@test=# select extract_hhmmss('2017-06-22 01:03:05.1212'::timestamptz, '2017-06-21'::timestamptz);
 extract_hhmmss
----------------
 -25:03:05
(1 row)

请注意,如果以相反的顺序提供时间戳,它将会出错,但它并不是很难修复。 //更新:已修复。

答案 1 :(得分:1)

你可以使用EXTRACT函数并用MAKE_INTERVAL和一些数学来包装它。它非常简单,因为你将时间戳的每个部分都传递给它:

select 
  make_interval(
    0,0,0,0, -- years, months, weeks, days
    extract(days from durdata)::int * 24 + extract(hours from durdata)::int, -- calculated hours (days * 24 + hours)
    extract(mins from durdata)::int, -- minutes
    floor(extract(secs from durdata))::int -- seconds, without miliseconds, thus FLOOR()
    ) as durdata
from (
  select '2017-06-22 02:06:41.993657'::timestamp - '2017-06-21'::timestamp
  ) t(durdata);

输出:

 durdata
----------
 26:06:41

您可以将其包装在一个函数中,以便于使用。 不用担心timestamp - timestamp精确地将输出返回到几天以上,从而丢失了一些信息,因为即使不同年份的计算仍会返回天数和额外时间部分。

示例:

postgres=# select ('2019-06-22 01:03:05.993657'::timestamp - '2017-06-21'::timestamp) as durdata;
        durdata
------------------------
 731 days 01:03:05.993657