为什么postgres显示相同间隔值的两种不同格式?

Jua*_*eza 5 sql postgresql pgadmin postgresql-9.5

我正在帮助解决这个问题,试图改变间隔的格式.

from '01 day 22:10:37'  to  '46:10:37'
Run Code Online (Sandbox Code Playgroud)

我给出一个字符串操作的解决方案.但后来我发现postgres可以在两种不同的格式上显示相同的间隔.

SELECT '2016-01-27 08:51:02'::timestamp - '2016-01-25 10:40:25'::timestamp end_date,
       '46:10:37'::interval interval_date;
Run Code Online (Sandbox Code Playgroud)

有趣的事情.有一个函数正在进行逆过程

 justify_hours('46:10:37'::interval) --> '1 day 22:10:37'
Run Code Online (Sandbox Code Playgroud)

所以我想知道是否有解决这个问题的直接方法.为什么相同的区间值有两个不同的结果.

pgAdmin输出:

在此输入图像描述

kli*_*lin 5

当一个时间间隔是两个时间戳之间的差异时,它总是以小时为单位(即它具有标准格式)。例子:

select
    '2015-01-01 13:0:0'::timestamp - '2014-01-01 23:0:0'::timestamp, --> 364 days 14:00:00
    '2015-01-01 13:0:0'::timestamp - '2014-01-01 03:0:0'::timestamp, --> 365 days 10:00:00
    '2015-01-01 13:0:0'::timestamp - '2015-01-01 03:0:0'::timestamp; --> 10:00:00
Run Code Online (Sandbox Code Playgroud)

间隔计算在日期部分和时间部分分别执行,因此它们可能会导致奇怪的格式。例子:

select 
    '2 day 1:00:00'::interval- '1 day 2:00:00'::interval,    --> 1 day -01:00:00 (!!)
    '2 day 100:00:00'::interval+ '1 day 60:00:00'::interval, --> 3 days 160:00:00
    '2 day 100:00:00'::interval- '2 day 60:00:00'::interval; --> 40:00:00
Run Code Online (Sandbox Code Playgroud)

对于这种情况,Postgres 开发人员为格式标准化提供了适当的功能:

select 
    justify_hours('1 day -01:00:00'),  --> 23:00:00
    justify_hours('3 days 160:00:00'), --> 9 days 16:00:00
    justify_hours('40:00:00');         --> 1 day 16:00:00
Run Code Online (Sandbox Code Playgroud)

然而,他们认为不需要反向操作。在这个答案中,我提出了一个将时间间隔的日期部分转换为小时的函数。我认为它可以(有一些细微的变化)某种反转功能justify_hours()

create or replace function unjustify_hours(interval)
returns interval language sql as $$
    select format('%s:%s',
        (extract (epoch from $1) / 3600)::int,
        to_char($1, 'mi:ss'))::interval;
$$;

select 
    unjustify_hours('23:00:00'),        --> 23:00:00
    unjustify_hours('9 days 16:00:00'), --> 232:00:00
    unjustify_hours('1 day 16:00:00');  --> 40:00:00
Run Code Online (Sandbox Code Playgroud)

该功能to_char(interval, text)在这里没有帮助,因为

select 
    to_char(interval '23:00:00', 'hh24:mi:ss'),        --> 23:00:00
    to_char(interval '9 days 16:00:00', 'hh24:mi:ss'), --> 16:00:00 (!)
    to_char(interval '1 day 16:00:00',  'hh24:mi:ss'); --> 16:00:00 (!)
Run Code Online (Sandbox Code Playgroud)

请注意,间隔可以通过多种方式正确格式化:

select 
    justify_hours('100:00:00'),        --> 4 days 04:00:00
    justify_hours('1 days 76:00:00'),  --> 4 days 04:00:00
    justify_hours('2 days 52:00:00'),  --> 4 days 04:00:00
    justify_hours('5 days -20:00:00'); --> 4 days 04:00:00
Run Code Online (Sandbox Code Playgroud)

根据文档

根据 SQL 标准,区间值的所有字段必须具有相同的符号,因此前导负号适用于所有字段;例如,间隔文字“-1 2:03:04”中的负号适用于天和小时/分钟/秒部分。PostgreSQL 允许字段具有不同的符号,并且传统上将文本表示中的每个字段视为独立签名,因此在此示例中时/分/秒部分被视为正数。如果 IntervalStyle 设置为 sql_standard,则认为前导符号适用于所有字段(但前提是没有出现其他符号)。否则使用传统的 PostgreSQL 解释。为避免歧义,如果任何字段为负,建议为每个字段附加一个明确的符号。

内部间隔值存储为月、日和秒。这样做是因为一个月中的天数各不相同,如果涉及夏令时调整,一天可能有 23 或 25 小时。月份和日期字段是整数,而秒字段可以存储分数。因为区间通常是从常量字符串或时间戳减法创建的,所以这种存储方法在大多数情况下都可以很好地工作。函数 justify_days 和 justify_hours 可用于调整超出其正常范围的天数和小时数。

  • 您好,我的意思并不是要改进功能。我刚刚将函数的结果类型从“text”更改为“interval”(将此函数与引用的答案的函数进行比较)。 (2认同)