2017-02-01 16 views
1

時間の経過とともに好きな色を追跡する表を考えてみましょう。オーバーラップをクリーンアップする有効期限 - 有効期限

drop table favourites; 

create table favourites(
    person_id varchar2(10) not null 
    ,valid_from date   not null 
    ,valid_to date 
    ,color  varchar2(10) not null 
    ,constraint favourites_pk primary key(person_id, valid_from) 
); 

insert into favourites values('Ronnie', date '1979-09-12', null,    'Green'); 
insert into favourites values('Ronnie', date '2000-01-01', date '2016-12-31', 'Blue'); 

commit; 

表は、「Ronnie」は1979-09-12から緑が好きで、2000-01-01からは青が好きです。 Ronnieが2017年の最初の日に起きたとき、彼はもはやBlueを好きではない。

1回限りのスクリプトでテーブルをクリーンアップする必要がありますが、上記の特定のケースに固執しています。これまでのベストアイデアはvalid_toの日付をvalid_fromの日付に基づいて再計算することですが、この場合、私は実際に情報を破壊します。以下に見られるように、 "green"はもはや2017-01-01のお気に入りではありません。

select person_id 
     ,valid_from  
     ,valid_to 
     ,lead(valid_from,1) over(partition by person_id order by valid_from)-1 as valid_to2 
     ,color   
    from favourites t; 


PERSON_ID VALID_FROM VALID_TO NEW_VALID_TO COLOR 
--------- ---------- ---------- ------------ ------ 
Ronnie  1979-09-12 null  1999-12-31  Green 
Ronnie  2000-01-01 2016-12-31 null   Blue 

最終結果がなるようにどのように私はaditionalレコードを生成できます。

PERSON_ID VALID_FROM VALID_TO COLOR 
--------- ---------- ---------- ------ 
Ronnie  1979-09-12 1999-12-31 Green 
Ronnie  2000-01-01 2016-12-31 Blue 
Ronnie  2017-01-01 null  Green 

編集 valid_to日付の背後には、合理的なロジックはありません。それはユーザーが入れているものなので、あらゆる種類のクレイジーなオーバーラップが存在します。 nullは「永遠」を意味し、9999-12-31は理解しやすい場合を意味します。別のテーブルを作成してこのデザインを修正したいのですが、まずは古いデータを修正する方法が必要です。

多分このように見ている方が簡単でしょう。

Green |----------------------------------> 
Blue   |------| 

私は情報を破壊し、新しいVALID_TO日を計算することによって、オーバーラップを修正:

Green |--------| 
Blue   |------| 

それはこのように固定することがあります。元のデータセットに

Green |--------| 
Blue   |------| 
Green     |--------> 

問題(重複のために)2番目のクエリのマニフェストのみ。他の2つは正しい結果を示しています。

select * 
    from favourites 
where (date '1995-01-01' >= valid_from) 
    and (date '1995-01-01' <= valid_to or valid_to is null); 

select * 
    from favourites 
where (date '2015-01-01' >= valid_from) 
    and (date '2015-01-01' <= valid_to or valid_to is null); 

select * 
    from favourites 
where (date '2025-01-01' >= valid_from) 
    and (date '2025-01-01' <= valid_to or valid_to is null); 
+0

これは、オーバーラップの唯一の例ですか? –

+1

新しく挿入された行の色が緑色になるのはなぜですか? nullでもかまいませんか? –

+0

彼は2017年に再び彼が好きになったことをどうやって知っていますか?どこかでデフォルトルールがありますか? – tbone

答えて

0

あなたの例は、あなたが気に重複の一種である場合は、union allでこれを処理することができます。私は一つだけvalid_fromNULLで、レコードがそう重ならないことを意味し、唯一のタイプ別

select person_id, color, valid_from, 
     coalesce(valid_to, lead(valid_from) over (partition by person_id order by valid_from)-1) as valid_to2 
from favourites t 
union all 
select person_id, color, valid_to + 1 as valid_from, NULL as valid_to 
from favourites f 
where valid_from is null and 
     exists (select 1 from favourites f2 where f2.person_id = f.person_id and f2.valid_from > f.valid_from); 

:私は、これはあなたが望むクエリだと思います。より多くの質問がある場合は、適切なデータと重複をどうするかについての説明で別の質問をする必要があります。

0

現在の行の終了日を取得するのは簡単です(次の行のdate_fromから1日を引く)。ただし、union allが必要な新しい行を生成する必要があります。また、デフォルトの色は、valid_tonullの行から選択されます。 (そうでない場合は、cteのロジックを変更する必要があります)。

WITH CTE AS 
(SELECT PERSON_ID, 
     COLOR, 
     VALID_FROM, 
     ROW_NUMBER() OVER(PARTITION BY PERSON_ID ORDER BY VALID_FROM DESC) AS RNUM, 
     COALESCE(VALID_TO, LEAD(VALID_FROM) OVER (PARTITION BY PERSON_ID 
                ORDER BY VALID_FROM)-1) AS VALID_TO_NEW, 
     MAX(CASE WHEN VALID_TO IS NULL THEN COLOR END) 
        OVER(PARTITION BY PERSON_ID ORDER BY VALID_FROM) AS DEFAULT_COLOR 
    FROM FAVOURITES) 
SELECT PERSON_ID, 
     VALID_FROM, 
     VALID_TO_NEW, 
     COLOR 
FROM CTE 
UNION ALL 
--Generates a new last row with the default color if it exists 
SELECT PERSON_ID, 
     VALID_TO_NEW+1, 
     NULL, 
     DEFAULT_COLOR 
FROM CTE 
WHERE RNUM=1 

Sample Demo