9

我有一个计算日期差异的 PostgreSQL 函数:

CREATE OR REPLACE FUNCTION testDateDiff () RETURNS int AS $BODY$
DECLARE startDate TIMESTAMP;
DECLARE endDate TIMESTAMP;
DECLARE diffDatePart int ;
BEGIN
Select evt_start_date From events Where evt_id = 5 INTO startDate ;
Select evt_start_date From events Where evt_id = 6 INTO  endDate ;
SELECT EXTRACT(day FROM TIMESTAMP startDate - endDate) INTO diffDatePart;
RETURN diffDatePart;
END;
$BODY$
LANGUAGE plpgsql 
COST 100

如果直接减去日期,则计算差异。但在我的情况下,日期存在于变量 asstartDateendDate中,这会导致问题。

如何减去变量中包含的日期?

4

3 回答 3

11

调试

您的功能正在做的事情可以做得简单。语法错误的实际原因在这里:

SELECT EXTRACT(day FROM TIMESTAMP startDate - endDate) INTO diffDatePart;

看起来您正在尝试强制startDate转换为timestamp,这从一开始就是无稽之谈,因为您的参数startDate已声明为timestamp

它也不起作用。我在这里引用手册

为避免语法歧义,type 'string' 语法只能用于指定简单文字常量的类型。

像这样工作:

SELECT EXTRACT(day FROM startDate - endDate)::int INTO diffDatePart;

但这仍然没有多大意义。您在谈论“日期”,但仍将您的参数定义为timestamp. 你可以像这样对你拥有的东西进行消毒:

CREATE OR REPLACE FUNCTION f_date_diff()
  RETURNS int AS
$BODY$
DECLARE
    start_date date;
    end_date   date;
    date_diff  int;
BEGIN
SELECT evt_start_date FROM events WHERE evt_id = 5 INTO start_date;
SELECT evt_start_date FROM events WHERE evt_id = 6 INTO end_date;
date_diff := (endDate - startDate);
RETURN date_diff;
END
$BODY$ LANGUAGE plpgsql;
  • DECLARE只需要一次。
  • date声明为正确类型的列date
  • 不要使用大小写混合的标识符,除非您确切地知道自己在做什么。
  • 结尾减去开头以获得正数或应用绝对值运算符@
  • 由于减去日期(而不是减去时间戳,它产生一个interval)已经产生integer,简化为:

    SELECT (startDate - endDate) INTO diffDatePart;
    

    甚至更简单的 plpgsql 赋值:

    diffDatePart := (startDate - endDate);
    

简单查询

您可以通过简单的查询来解决简单的任务 - 使用子查询:

SELECT (SELECT evt_start_date
        FROM   events
        WHERE  evt_id = 6) 
      - evt_start_date AS date_diff
FROM   events
WHERE  evt_id = 5;

或者您可以CROSS JOIN将基表设置为自身(每个实例的 1 行,所以没关系):

SELECT e.evt_start_date - s.evt_start_date AS date_diff
FROM   events e
      ,events s
WHERE  e.evt_id = 6
AND    s.evt_id = 5;

SQL 函数

如果您坚持使用某个函数,请使用一个简单的 sql 函数:

CREATE OR REPLACE FUNCTION f_date_diff(_start_id int, _end_id int)
  RETURNS int LANGUAGE sql AS
$func$
SELECT e.evt_start_date - s.evt_start_date
FROM   events s, events e
WHERE  s.evt_id = $1
AND    e.evt_id = $2
$func$;

称呼:

SELECT  f_date_diff(5, 6);

PL/pgSQL 函数

如果您坚持使用 plpgsql ...

CREATE OR REPLACE FUNCTION f_date_diff(_start_id int, _end_id int)
  RETURNS int LANGUAGE plpgsql AS
$func$
BEGIN

RETURN (SELECT evt_start_date 
             - (SELECT evt_start_date FROM events WHERE evt_id = _start_id)
        FROM   events WHERE evt_id = _end_id);
END
$func$;

同样的调用。

于 2012-12-30T15:40:34.587 回答
1

我会这样写查询:

create function testDateDiff()
  returns integer as $$
  declare 
    startDate timestamp;
    endDate timestamp;
  begin
    startDate := (select evt_start_date From events Where evt_id = 5);
    endDate   := (select evt_start_date From events Where evt_id = 6);
    return (select extract(day from startDate - endDate));
  end;
  $$ language 'plpgsql';

在上面的上下文中使用:=和之间的区别在于,使用你的查询必须返回一个值。如果您使用您的查询可以返回单行(即多于一列)。into:=into

有关使用selectwithinto和 plpgsql 的完整说明,您应该阅读http://www.postgresql.org/docs/9.1/static/plpgsql-statements.html。具体来说,PostgreSQL 文档的第 39.5.3 节。

于 2012-12-29T00:09:59.730 回答
1

你真的需要一个函数吗?

这个查询也可以工作:

SELECT (SELECT evt_start_date::date FROM events WHERE evt_id = 5) 
        - evt_start_date::date 
        FROM events WHERE evt_id = 6;
于 2012-12-31T05:06:10.707 回答