我有一个字段,指定将来的时间戳,记录的到期时间。我希望能够接受字符串时间戳或表示从现在开始的时间间隔。是否有一些简单的方法尝试将字符串转换为间隔,如果成功则将其添加到now()
,否则将其转换为时间戳(带时区)?
在被问到之前,是的,我确信我可以使用存储过程来完成它,我很好奇我是否可以使用某种类型的COALESCE
样式魔法。如果答案是存储过程那么就是这样,我可以写那个。
编辑:这是我作为存储过程的实现。
create or replace function
timestamptz_in_or_at(input text)
returns timestamptz
as $$
begin
begin
return now() + input::interval;
exception when others then
return input::timestamptz;
end;
end;
$$ language plpgsql
答案 0 :(得分:1)
我认为您唯一的机会是使用正则表达式验证间隔字符串,因为CAST会在无效字符串上引发错误,而make_interval也没有得到字符串输入的签名。
间隔输入格式为known。您可以在postgres_verbose或ISO格式中编写所需精度的正则表达式。
例如:
SELECT
CASE WHEN your_text_column ~* '^(\d+ (minute|minutes|second|seconds)\ *)+$'
THEN now() + your_text_column::interval
ELSE your_text_column::timestamptz END
FROM test;
在这种情况下,您必须从表格的文本类型列中读取间隔/时间戳字符串。另一方面,当您的输入字符串来自用户输入时,查询将变得更加复杂。您必须处理尝试优化常量值的规划器,即使在CASE的假分支中也是如此。为了防止这种情况,您必须在其中一个表达式中引入volatile函数:
SELECT
CAST(
CASE WHEN ? ~* '^(\d+ (minute|minutes|second|seconds)\ *)+$'
THEN CAST(now() + CAST((? || repeat('',(random()*0)::integer)) AS interval) AS text)
ELSE ? END
AS timestamptz);
首先,CASE的分支必须返回相同的类型。时间间隔和时间戳不兼容,但text
是好的。其次,在任何一种情况下,其中一个CAST都会失败,除非您可以将评估推迟到运行时阶段。
在区间分支中,|| repeat('',(random()*0)::integer
不向字符串添加任何内容,但会引入波动性并停止计划程序评估可能错误的区间文本。
外部CAST将CASE的ELSE分支的评估推迟到运行时,确定该字符串不包含间隔。