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()it を initialに統合する必要があります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 に答える