Sql 在Postgres函数中显示下一个半小时的时间点
我试图创建一个函数来显示下一个半小时的时间 所以当当前时间是13:40时,我希望它显示14:00而不是13:30 我所创造的,在最接近的半小时内完成工作,而不是在未来最接近的时间内:Sql 在Postgres函数中显示下一个半小时的时间点,sql,postgresql,postgresql-9.1,Sql,Postgresql,Postgresql 9.1,我试图创建一个函数来显示下一个半小时的时间 所以当当前时间是13:40时,我希望它显示14:00而不是13:30 我所创造的,在最接近的半小时内完成工作,而不是在未来最接近的时间内: CREATE OR REPLACE FUNCTION round_timestamp( ts timestamptz ,round_secs int ) RETURNS timestamptz AS $$ DECLARE
CREATE OR REPLACE FUNCTION round_timestamp(
ts timestamptz
,round_secs int
) RETURNS timestamptz AS $$
DECLARE
_mystamp timestamp;
_round_secs decimal;
BEGIN
_round_secs := round_secs::decimal;
_mystamp := timestamptz 'epoch'
+ ROUND((EXTRACT(EPOCH FROM ts))::int / _round_secs) * _round_secs
* INTERVAL '1 second';
RETURN _mystamp;
END; $$ LANGUAGE plpgsql IMMUTABLE;
关于如何使此功能显示未来最接近的半小时间隔,您有什么想法吗?您可以分别使用向上和向下取整
如果您的函数如此简单,那么您也可以用语言SQL
编写它,这使得它更简单/可读(并且解析器也可以对其进行规划):
简单地说,在向上缩放到时间戳之前,向向下舍入的历元添加1:
CREATE OR REPLACE FUNCTION round_timestamp(ts timestamptz, round_secs int)
RETURNS timestamptz AS $$
BEGIN
RETURN timestamptz 'epoch'
+ (ROUND((EXTRACT(EPOCH FROM ts))::int / round_secs) + 1) * round_secs
* INTERVAL '1 second';
END; $$ LANGUAGE plpgsql IMMUTABLE;
不需要使用局部变量。为了避免在使用历元和浮点算法时出错,您可以使用日期算法,另外还有一个好处,就是可以更清楚地了解发生了什么:
create or replace function round_tstz(ts timestamptz)
returns timestamptz
as $$
select date_trunc('hour', $1) +
-- what hour will it be in 30 min?
case date_trunc('hour', $1 + interval '30 min')
-- the same: round to next half hour
when date_trunc('hour', $1) then interval '30 min'
-- not the same: round to next hour
else interval '1 hour'
end;
$$ language sql stable;
# select now()::timestamptz(0);
now
------------------------
2014-12-05 14:34:30+01
(1 row)
# select round_tstz(now()), round_tstz(now() + interval '30 min');
round_tstz | round_tstz
------------------------+------------------------
2014-12-05 15:00:00+01 | 2014-12-05 15:30:00+01
(1 row)
您需要将
X
minutes添加到时间戳中的当前分钟数M
,其中:
X=30-M%30-30*(M%30)=0)::int
将除法的余数从30减去30,然后仅当余数为0时,再减去30,以便保留:00和:30的值,并且不进行四舍五入
例如,如果时间是14:11,那么M=11,您需要添加19分钟才能到达14:30(30-11%30=19),对于14:47,M=47,您需要添加13分钟才能到达15:00(30-47%30=13)。该规则的唯一例外是余数为0的情况,即14:30、15:00等小时数,这就是公式中的最后一项起作用的地方——减去30*的乘积(0或1,取决于除法的余数为30)
考虑到时间戳本身是双精度的,我怀疑这种方法会有很大的不同。与简单的历元提取和(通常)硬件优化舍入相比,由于
timestamp\u trunc()
中的所有逻辑,它的性能可能更差。@Patrick:可能是。。。我可以想象,timestamp_trunc()中的逻辑是C语言,它只会被计算两次,因为优化器可以跳过其中一个函数调用,因为它是稳定的,它比不调用它(包括除法等等)而产生的所有浮点运算要快一点。无论如何,我认为这一点在一天结束时并不重要:我认为重要的是,我所建议的方法是可读性和可理解性的好几倍。但这只是我自己的看法。:-)阅读你的例子,这只是你一天的开始。。。你在凌晨4:26穿这样的衣服干什么???撇开工作时间不谈,timestamp_trunc()的所有变体都以一个大的case语句开始,以评估感兴趣的时间段<代码>示例中的“小时”。在OP的问题上,最好是在不考虑PG的情况下直接讨论第二个问题。@Patrick:呵呵。我的笔记本电脑服务器配置错误:-无论如何,我还没有检查源代码,所以只能相信你的话。我仍然认为最好是编写代码,就好像下一个家伙是一个拿着斧头的精神病患者,他知道你住在哪里;尽管我试图对提取划时代之类的东西持开放态度,但我发现我自己的建议,或者Kouper刚刚发布的建议,让事情变得更加直观。是的,代码清晰性很重要。不过有一个复杂的问题:虽然OP提到了半小时的间隔,但他的代码示例使用了任意秒数来取整。所以如果他想要1864秒,那么你的代码就不能精确到31分4秒。但这对我来说只是一个小问题。如果我当前的时间是15:25,这个函数会使它变成15:30吗?在你的服务器上运行它。如果您为舍入秒指定1800,那么它将是。因为时间戳具有微秒分辨率,所以将“舍入”值舍入的问题高度依赖于应用程序,我猜这与此无关。
create or replace function round_tstz(ts timestamptz)
returns timestamptz
as $$
select date_trunc('hour', $1) +
-- what hour will it be in 30 min?
case date_trunc('hour', $1 + interval '30 min')
-- the same: round to next half hour
when date_trunc('hour', $1) then interval '30 min'
-- not the same: round to next hour
else interval '1 hour'
end;
$$ language sql stable;
# select now()::timestamptz(0);
now
------------------------
2014-12-05 14:34:30+01
(1 row)
# select round_tstz(now()), round_tstz(now() + interval '30 min');
round_tstz | round_tstz
------------------------+------------------------
2014-12-05 15:00:00+01 | 2014-12-05 15:30:00+01
(1 row)
CREATE FUNCTION round_up(timestamptz) RETURNS timestamptz AS $$
SELECT
$1 + '1 minute'::interval * (
30 - (EXTRACT('minute' FROM $1)::int % 30)
- 30 * ((EXTRACT('minute' FROM $1)::int % 30)=0)::int
);
$$ LANGUAGE sql IMMUTABLE;