2017-08-31 207 views
0

我有一个字段指定了将来的时间戳,记录的到期时间。我希望能够接受字符串时间戳或表示从现在开始的时间间隔。是否有一些简单的方法来尝试将字符串转换为间隔,如果成功,则将其添加到now(),否则将其转换为时间戳(带时区)?强制将时间戳或时间间隔字符串转换为时间戳

在它被问到之前,是的,我确定我可以用存储过程执行它,我很好奇我是否可以使用某种类型的COALESCE样式magick。如果答案是存储过程,那就这样吧,我可以写出这个答案。


编辑:这是我作为存储过程的实现。

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 
+2

像这样的问题是错误处理。如果你只是在里面插入一个强制类型,而且字符串不正确,你会得到一个SQL错误。如果您想查看是否可以在不破坏当前事务的情况下进行强制转换,那么您可以在SQL中使用保存点,也可以编写带有错误处理功能的plpgsql函数。两者都很好。 –

回答

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的错误分支中也是如此。为了防止这种情况,你必须引入挥发性功能到表现之一:

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分支的评估推迟到运行时确定该字符串不包含间隔。

+0

谢谢你的努力。我想你已经证实,在所有的实践中我所希望的是不可能的。我将我的存储过程添加为编辑,我认为它显示了总体上更简单。再次感谢! –

+0

我同意,存储过程是完成此操作的最佳方法。 –