2017-01-06 4 views
2

person単位の日付とそのシーケンスの合計amountで、連続したシーケンスを特定しようとしています。私records表には、次のようになります。開始日と終了日からシーケンスを特定して集計する方法

person start_date end_date  amount 
1  2015-09-10 2015-09-11 500 
1  2015-09-11 2015-09-12 100 
1  2015-09-13 2015-09-14 200 
1  2015-10-05 2015-10-07 2000 
2  2015-10-05 2015-10-05 300 
2  2015-10-06 2015-10-06 1000 
3  2015-04-23 2015-04-23 900 

結果のクエリは、このようになります。以下は

person sequence_start_date sequence_end_date  amount 
1  2015-09-10   2015-09-14   800 
1  2015-10-05   2015-10-07   2000 
2  2015-10-05   2015-10-06   1400 
3  2015-04-23   2015-04-23   900 

、私はシーケンスstart_dateend_dateを識別するために、LAGおよびLEADを使用することができますが、私が持っていませんamountを集計する方法。私は答えはシーケンスによって分割されるROW_NUMBER()ウィンドウ関数のいくつかの並べ替えを含むと仮定している、私はちょうどシーケンスシーケンスを関数に識別可能にする方法を見つけることができません。

SELECT 
person 
,COALESCE(sequence_start_date, LAG(sequence_start_date, 1) OVER (ORDER BY person, start_date)) AS "sequence_start_date" 
,COALESCE(sequence_end_date, LEAD(sequence_end_date, 1) OVER (ORDER BY person, start_date)) AS "sequence_end_date" 
FROM 
(
SELECT 
    person 
    ,start_date 
    ,end_date 
    ,CASE WHEN LAG(end_date, 1) OVER (PARTITION BY person ORDER BY start_date) + interval '1 day' = start_date 
    THEN NULL 
    ELSE start_date 
    END AS "sequence_start_date" 
    ,CASE WHEN LEAD(start_date, 1) OVER (PARTITION BY person ORDER BY start_date) - interval '1 day' = end_date 
    THEN NULL 
    ELSE end_date 
    END AS "sequence_end_date" 
    ,amount 
FROM records 
) sq 
+0

既存のクエリのサブクエリは、それらのカラム名と矛盾する 'sequence_start_date'と' sequence_end_date'値を生成します。 –

+0

あなたのデータは少し矛盾しているようです。場合によっては、同じ行の前の行の終了日と同じ行の開始日と同じですが、それ以外の場合は開始日が前の終了日の1日後です。これはデータのエラーですか、どちらのケースも対応する必要がありますか? –

答えて

1

であっても、あなたの更新(サブ)クエリがまだデータ用に非常に適切ではありませんあなたは第二の開始日かどうかについて、矛盾している、提示してきたとシーケンス内の後続の行は、前の行の終了日に等しいか、または1日後に一致する必要があります。それが必要な場合は、両方に対応できるようにクエリをかなり簡単に更新することができます。

いずれにしても、ウィンドウ関数としてCOALESCEを使用することはできません。集計関数は、OVER節を提供することによってウィンドウ関数として使用できますが、通常の関数は使用できません。それにもかかわらず、このタスクにウィンドウ関数を適用する方法があります。ここでは(提示した)データ内の配列を同定するための方法です:

SELECT 
    person 
    ,MAX(sequence_start_date) 
    OVER (
     PARTITION BY person 
     ORDER BY start_date 
     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 
    AS "sequence_start_date" 
    ,MIN(sequence_end_date) 
    OVER (
     PARTITION BY person 
     ORDER BY start_date 
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) 
    AS "sequence_end_date" 
    ,amount 
FROM 
(
SELECT 
    person 
    ,start_date 
    ,end_date 
    ,CASE WHEN LAG(end_date, 1) OVER (PARTITION BY person ORDER BY start_date) + interval '1 day' >= start_date 
    THEN date '0001-01-01' 
    ELSE start_date 
    END AS "sequence_start_date" 
    ,CASE WHEN LEAD(start_date, 1) OVER (PARTITION BY person ORDER BY start_date) - interval '1 day' <= end_date 
    THEN NULL 
    ELSE end_date 
    END AS "sequence_end_date" 
    ,amount 
FROM records 
order by person, start_date 
) sq_part 
ORDER BY person, sequence_start_date 

MAX()、代わりにCOALESCE()MIN()に依存している、そしてそれは、各パーティション内のもののそれぞれについて、適切な範囲を取得するには、ウィンドウのフレーミングを適用します。結果:

person sequence_start_date   sequence_end_date   amount 
1  September, 10 2015 00:00:00 September, 12 2015 00:00:00 500 
1  September, 10 2015 00:00:00 September, 12 2015 00:00:00 100 
1  October, 05 2015 00:00:00 October, 07 2015 00:00:00 2000 
2  October, 05 2015 00:00:00 October, 06 2015 00:00:00 300 
2  October, 05 2015 00:00:00 October, 06 2015 00:00:00 1000 
3  April, 23 2015 00:00:00  April, 23 2015 00:00:00  900 

終了日とその後の開始日とは完全に一致する必要はありません。 または重複するの各人物のすべての行が同じシーケンスに割り当てられます。ただし、(personstart_date)を一意にすることができない場合は、終了日までにパーティションを並べ替える必要があります。

これで配列を特定する方法が得られました。これらはトリプルperson, sequence_start_date, sequence_end_dateによって特徴付けられます。

SELECT 
    person, 
    sequence_start_date, 
    sequence_end_date, 
    SUM(amount) AS "amount" 
FROM (<above query>) sq 
GROUP BY person, sequence_start_date, sequence_end_date 
:(。または実際に、あなただけの人と 識別目的のためにこれらの日付の1が必要ですが、上の読み)あなたは所望の結果を得るために、外側集計クエリのインライン・ビューとして上記のクエリをラップすることができます

もちろん、それらを選択する場合は、両方の日付をグループ化する必要があります。

1

ない理由:

select a1.person, a1.sequence_start_date, a1.sequence_end_date, 
     sum(rx.amount) 
     as amount 
from (EXISTING_QUERY) a1 
left join records rx 
    on rx.person = a1.person 
    and rx.start_date >= a1.start_date 
    and rx.end_date <= a1.end_date 
group by a1.person, a1.sequence_start_date, a1.sequence_end_date 
関連する問題