当前位置:  开发笔记 > 编程语言 > 正文

Postgres now()vs'now'in function

如何解决《Postgresnow()vs'now'infunction》经验,为你挑选了1个好方法。

遇到在Postgres中的函数中使用now()与'now'不同的问题.

drop table if exists test_date_bug;
CREATE TABLE test_date_bug
(
id serial NOT NULL,
date1 timestamp with time zone NOT NULL DEFAULT current_timestamp,
date2 timestamp with time zone NOT NULL DEFAULT 'infinity'
)
WITH (
OIDS=FALSE
);

drop function if exists test_date_bug_function(id_param bigint); 
CREATE OR REPLACE FUNCTION test_date_bug_function(id_param bigint)
RETURNS void AS
$$ 
BEGIN 
   UPDATE test_date_bug SET date2 = 'now' WHERE id = id_param;  
END;
$$
LANGUAGE 'plpgsql' VOLATILE
SECURITY DEFINER
SET search_path = public, pg_temp;  

insert into test_date_bug DEFAULT VALUES; 
insert into test_date_bug DEFAULT VALUES;  
insert into test_date_bug DEFAULT VALUES; 

select 1 from test_date_bug_function(1);

等几秒钟

select 1 from test_date_bug_function(2);

结果:

select * from test_date_bug; 
id |             date1             |             date2             
----+-------------------------------+-------------------------------
3 | 2015-12-10 12:42:01.931554-06 | infinity
1 | 2015-12-10 12:42:01.334465-06 | 2015-12-10 12:42:09.491183-06
2 | 2015-12-10 12:42:01.335665-06 | 2015-12-10 12:42:09.491183-06
(3 rows)

我不希望第2行的date2与第1行的date2相同.

更换

 UPDATE test_date_bug SET date2 = 'now' WHERE id = id_param;

 UPDATE test_date_bug SET date2 = now() WHERE id = id_param;

按照我的预期设置新日期:

select * from test_date_bug; 
id |             date1             |             date2             
----+-------------------------------+-------------------------------
3 | 2015-12-10 12:43:29.480242-06 | infinity
1 | 2015-12-10 12:43:28.451195-06 | 2015-12-10 12:43:38.496625-06
2 | 2015-12-10 12:43:28.451786-06 | 2015-12-10 12:43:43.447715-06

想法?



1> Egor Rogov..:

这不是一个错误,它是一个功能......这里有两点.

    替换'现在'

    我们来看看文档(日期/时间函数和运算符):

    所有日期/时间数据类型现在也接受特殊文字值以指定当前日期和时间(再次,解释为事务开始时间).因此,以下三个都返回相同的结果:

    SELECT CURRENT_TIMESTAMP;

    SELECT now();

    选择TIMESTAMP'现在'; - 与DEFAULT一起使用不正确

    提示:在创建表时,您不希望在指定DEFAULT子句时使用第三种形式.一旦解析了常量,系统现在将转换为时间戳,因此当需要默认值时,将使用表创建的时间!在使用默认值之前,不会评估前两个表单,因为它们是函数调用.因此,它们将提供所需的默认行为到行插入的时间.

    因此'now'在解析时转换为时间戳.

    准备好的陈述

    好的,但它在功能方面意味着什么?每次调用函数时都很容易证明函数被解释:

    t=# create function test() returns timestamp as $$
    begin
     return 'now';
    end;
    $$ language plpgsql;
    CREATE FUNCTION
    
    t=# select test();
               test            
    ----------------------------
     2015-12-11 11:14:43.479809
    (1 row)
    
    t=# select test();
               test            
    ----------------------------
     2015-12-11 11:14:47.350266
    (1 row)
    

    在此示例中,'now'行为与您预期的一样.

    有什么不同?您的函数使用SQL语句,而test()则不使用.让我们再看一下文档(PL/pgSQL Plan Caching):

    当每个表达式和SQL命令首先在函数中执行时,PL/pgSQL解释器会解析并分析命令以创建预准备语句.

    在这里(准备声明):

    PREPARE创建一个准备好的声明.预准备语句是服务器端对象,可用于优化性能.执行PREPARE语句时,将解析,分析和重写指定的语句.随后发出EXECUTE命令时,将计划并执行准备好的语句.这种分工避免了重复的分析分析工作,同时允许执行计划依赖于提供的特定参数值.

    因此'now',在解析准备好的语句时将其转换为时间戳.让我们通过在函数之外创建一个预准备语句来证明这一点:

    t=# prepare s(integer) as UPDATE test_date_bug SET date2 = 'now' WHERE id = $1;
    PREPARE
    
    t=# execute s(1);
    UPDATE 1
    t=# execute s(2);
    UPDATE 1
    
    t=# select * from test_date_bug;
     id |             date1             |             date2
    ----+-------------------------------+-------------------------------
      3 | 2015-12-11 11:01:38.491656+03 | infinity
      1 | 2015-12-11 11:01:37.91818+03  | 2015-12-11 11:40:44.339623+03
      2 | 2015-12-11 11:01:37.931056+03 | 2015-12-11 11:40:44.339623+03
    (3 rows)
    

这就是发生的事情.'now'转换为时间戳一次(当解析准备好的语句时),now()并被调用两次.

推荐阅读
雯颜哥_135
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有