2

我需要检查上一条记录的元素,以确保我查询的日期不在结束日期和开始日期前 7 天之间的特定范围内。我有以下代码:

create or replace function eight (date) returns text as $$
declare
  r record;
  checkDate alias for $1;
begin
    for r in
    select * from periods
    order by startDate
  loop
    if (checkDate between r.startDate and r.endDate) then
      return q3(r.id);
    elsif (checkDate between (r.startDate - interval '7 days') and r.startDate) then
      return q3(r.id);
    elsif (checkDate between (lag(r.endDate) over (order by r.startDate)) and (r.startDate - interval '8 days')) then
      return q3(r.id);
    end if;
  end loop;
  return null;
end;
$$ language plpgsql;

所以基本上,我需要检查以下内容:

  • 如果查询日期在开始日期和结束日期之间

  • 如果查询日期是开始日期开始前 7 天

  • 如果查询日期在结束日期和开始日期之间,则返回与该日期关联的 id。

在大多数情况下,我的函数似乎工作正常,但有些情况似乎给了我 0 个结果(当应该总是有 1 个结果时)我的函数中是否缺少某些东西?我对最后一个 if 语句持怀疑态度。也就是说,尝试从以前的记录结束日期检查到当前记录的开始日期(间隔 7 天)

编辑:没有日期重叠。

4

3 回答 3

3

编辑:删除了关于 RETURN NEXT 的部分 - 我在那里误读了这个问题。
不能按照你的方式工作。不能这样调用窗口函数。您的记录变量就像循环r中的内置游标。FOR只有结果的当前行在循环内可见。您必须将窗口功能lag()集成到初始SELECT.

但是,由于无论如何您都在以匹配的顺序遍历行,因此您可以采用另一种方式。

考虑这个在很大程度上重写的例子。返回第一个违规行:

CREATE OR REPLACE FUNCTION q8(_day date)
  RETURNS text AS
$BODY$
DECLARE
    r            record;
    last_enddate date;

BEGIN
FOR r IN
    SELECT *
       -- ,lag(r.endDate) OVER (ORDER BY startDate) AS last_enddate
       -- commented, because I supply an alternative solution
    FROM   periods
    ORDER  BY startDate
LOOP
    IF _day BETWEEN r.startDate AND r.endDate THEN
        RETURN 'Violates condition 1';  -- I return differing results
    ELSIF _day BETWEEN (r.startDate - 7) AND r.startDate THEN
        RETURN 'Violates condition 2';
    ELSIF _day BETWEEN last_enddate AND (r.startDate) THEN 
                                      -- removed "- 7 ", that is covered above
        RETURN 'Violates condition 3';
    END IF;

    last_enddate := r.enddate; -- remember for next iteration
END LOOP;

RETURN NULL;

END;
$BODY$ LANGUAGE plpgsql;

更多提示

  • 为什么要使用别名$1?您已经在声明中命名了它_day。坚持下去。
  • 一定要知道 PostgreSQL 如何处理标识符中的大小写。(我只使用小写。)
  • 您可以从日期中添加/减去整数(天数)。
于 2012-04-21T12:48:47.733 回答
0

你确定这lag()会给你一些回报吗?我很确定这在这里是断章取义的。鉴于periods按顺序选择来自的行,您可以将当前存储startDate在一个变量中,并在下一个循环的 if 语句中使用它。

于 2012-04-21T11:02:50.917 回答
0
SET search_path='tmp';
DROP table period;
CREATE table period
        ( start_date DATE NOT NULL
        , end_date DATE
        );
INSERT INTO period(start_date ,end_date) VALUES
 ( '2012-01-01' , '2012-02-01' )
 , ( '2012-02-01' , '2012-02-07' )
 , ( '2012-03-01' , '2012-03-15' )
 , ( '2012-04-01' , NULL )
 , ( '2012-04-17' , '2012-04-21' )
        ;

DROP FUNCTION valid_date(DATE) ;
CREATE FUNCTION valid_date(DATE) RETURNS boolean
AS $body$
declare
        found boolean ;
        zdate ALIAS FOR $1;
begin
found = false;
SELECT true INTO found
        WHERE EXISTS (
        SELECT * FROM period p
        WHERE (p.start_date > zdate
                AND p.start_date < zdate + interval '7 day' )
        OR ( p.start_date < zdate AND p.end_date > zdate )
        OR ( p.start_date < zdate AND p.end_date IS NULL
                AND p.start_date >= zdate - interval '7 day' )
        )
        ;
if (found  = true) then
        return false;
else
        return true;
end if;
end;
$body$ LANGUAGE plpgsql;

\echo 2011-01-01:true
SELECT valid_date('2011-01-01' );
\echo 2012-04-08:false
SELECT valid_date('2012-04-08' );
\echo 2012-04-30:true
SELECT valid_date('2012-04-30' );

顺便说一句:我真的认为所需的功能应该实现为表约束,由触发器函数(可能基于上述函数)强加。

于 2012-04-21T12:21:35.027 回答