回答
create table so (
person varchar(32)
,country varchar(32)
,department varchar(32)
,login_time date
) distribute on random;
insert into so values ('Bob','CANADA','Marketing','2009-01-01');
insert into so values ('Bob','CANADA','Marketing','2009-02-01');
insert into so values ('Bob','USA','Marketing','2009-03-01');
insert into so values ('Bob','USA','Sales','2009-04-01');
insert into so values ('Bob','MEX','Product','2009-05-01');
insert into so values ('Bob','MEX','Product','2009-06-01');
insert into so values ('Bob','MEX','Product','2009-07-01');
insert into so values ('Bob','CANADA','Marketing','2009-08-01');
/* ************************************************************************** */
with prm as (--Create an ordinal primary key.
select
*
,row_number() over (
partition by person
order by login_time
) rwn
from
so
), chn as (--Chain events to their previous and next event.
select
cur.rwn
,cur.person
,cur.country
,cur.department
,cur.login_time cur_login
,case
when
cur.country = prv.country
and cur.department = prv.department
then 1
else 0
end prv_equal
,case
when
(
cur.country = nxt.country
and cur.department = nxt.department
) or nxt.rwn is null --No next record should be equivalent to matching.
then 1
else 0
end nxt_equal
,case prv_equal
when 0 then cur_login
else null
end eff_login_start_sparse
,case
when eff_login_start_sparse is null
then max(eff_login_start_sparse) over (
partition by cur.person
order by rwn
rows unbounded preceding --The secret sauce.
)
else eff_login_start_sparse
end eff_login_start
,case nxt_equal
when 0 then cur_login
else null
end eff_login_end
from
prm cur
left outer join prm nxt on
cur.person = nxt.person
and cur.rwn + 1 = nxt.rwn
left outer join prm prv on
cur.person = prv.person
and cur.rwn - 1 = prv.rwn
), grp as (--Group by login starts.
select
person
,country
,department
,eff_login_start
,max(eff_login_end) eff_login_end
from
chn
group by
person
,country
,department
,eff_login_start
), led as (--Change the effective end to be the next start, if desired.
select
person
,country
,department
,eff_login_start
,case
when eff_login_end is null
then null
else
lead(eff_login_start) over (
partition by person
order by eff_login_start
)
end eff_login_end
from
grp
)
select * from led order by eff_login_start;
のロジックを使用しての理にかなって、正しいですか?思考
このコードは、次の表を返します。
PERSON | COUNTRY | DEPARTMENT | EFF_LOGIN_START | EFF_LOGIN_END
--------+---------+------------+-----------------+---------------
Bob | CANADA | Marketing | 2009-01-01 | 2009-03-01
Bob | USA | Marketing | 2009-03-01 | 2009-04-01
Bob | USA | Sales | 2009-04-01 | 2009-05-01
Bob | MEX | Product | 2009-05-01 | 2009-08-01
Bob | CANADA | Marketing | 2009-08-01 |
説明
私は、過去数年間にこの四、五回を解決し、正式にそれを書き留めておく無視している必要があります。私はそれを行うチャンスを持ってうれしいので、これは大きな質問です。
これを試してみると、問題を行列形式で書き留めておきたいと思います。入力は、すべての値がSCDの同じキーを持つと仮定します。私たちは、キー値の上に全体の時間を分割することがあります。Cvは、我々はSCDのキー値は、このデータでは同じであると仮定、もう一度(と比較する必要があります値です
Cv | Ce
----|----
A | 10
A | 11
B | 14
C | 16
D | 18
D | 25
D | 34
A | 40
解とは無関係です)、Ceはイベント時間です。
まず、序数の主キーが必要です。私はテーブルにこのCkを指定しました。これにより、前と次のイベントを取得するためにテーブルに参加することができます。私はこれらの列をPk(前のキー)、Nk(次のキー)、Pv、Nvと呼んでいます。
Cv | Ce | Ck | Pk | Pv | Nk | Nv |
----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A |
A | 11 | 2 | 1 | A | 3 | B |
B | 14 | 3 | 2 | A | 4 | C |
C | 16 | 4 | 3 | B | 5 | D |
D | 18 | 5 | 4 | C | 6 | D |
D | 25 | 6 | 5 | D | 7 | D |
D | 34 | 7 | 6 | D | 8 | A |
A | 40 | 8 | 7 | D | | |
今、私たちは、私たちが連続イベントブロックの最初や最後にしているかどうかを確認するためにいくつかの列が必要です。私はこれらのPcとNcを連続して呼びます。 PcはPv = Cv => trueと定義される。 1は真を表し、0は偽を表す。 NCは(私たちは、なぜ分で表示されます)trueにヌルの場合のデフォルトことを除いて、同様に定義されて
Cv | Ce | Ck | Pk | Pv | Nk | Nv | Pc | Nc |
----|----|----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A | 0 | 1 |
A | 11 | 2 | 1 | A | 3 | B | 1 | 0 |
B | 14 | 3 | 2 | A | 4 | C | 0 | 0 |
C | 16 | 4 | 3 | B | 5 | D | 0 | 0 |
D | 18 | 5 | 4 | C | 6 | D | 0 | 1 |
D | 25 | 6 | 5 | D | 7 | D | 1 | 1 |
D | 34 | 7 | 6 | D | 8 | A | 1 | 0 |
A | 40 | 8 | 7 | D | | | 0 | 1 |
今、あなたは、PC、NCの1,1の組み合わせが完全に無用であるかを確認するために開始することができます記録。6行目のBobのMex/Productの組み合わせは、SCDを構築するときにはほとんど役に立たない情報なので、直感的にわかります。
だから、無駄な情報を取り除きましょう。ここでは2つの新しい列を追加します:Snと呼ばれるほぼ完全な有効開始時間とEeという実際に完了した有効終了時間。 Pcが0で、EeとをCEに取り込まれNcが0
Cv | Ce | Ck | Pk | Pv | Nk | Nv | Pc | Nc | Sn | Ee |
----|----|----|----|----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A | 0 | 1 | 10 | |
A | 11 | 2 | 1 | A | 3 | B | 1 | 0 | | 11 |
B | 14 | 3 | 2 | A | 4 | C | 0 | 0 | 14 | 14 |
C | 16 | 4 | 3 | B | 5 | D | 0 | 0 | 16 | 16 |
D | 18 | 5 | 4 | C | 6 | D | 0 | 1 | 18 | |
D | 25 | 6 | 5 | D | 7 | D | 1 | 1 | | |
D | 34 | 7 | 6 | D | 8 | A | 1 | 0 | | 34 |
A | 40 | 8 | 7 | D | | | 0 | 1 | 40 | |
あるときにこれが本当に近いように見えますが、我々はまだ(CVによって、我々はグループができないという問題点があるときSnがCeのが移入されています人/国/部門)。私たちが必要とするのは、Snがすべてのそれらのヌルに以前のSnの値を設定することです。 rwn < rwn
でこのテーブルに参加して最大値を得ることができますが、私は怠け者になり、Netezzaの解析関数とrows unbounded preceding
節を使用します。これは私が今説明した方法のショートカットです。それで、我々は次のように定義されたEs、efffective startという別の列を作成します。
case
when Sn is null
then max(Sn) over (
partition by k --key value of the SCD
order by Ck
rows unbounded preceding
)
else Sn
end Es
この定義では、これを取得します。
Cv | Ce | Ck | Pk | Pv | Nk | Nv | Pc | Nc | Sn | Ee | Es |
----|----|----|----|----|----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A | 0 | 1 | 10 | | 10 |
A | 11 | 2 | 1 | A | 3 | B | 1 | 0 | | 11 | 10 |
B | 14 | 3 | 2 | A | 4 | C | 0 | 0 | 14 | 14 | 14 |
C | 16 | 4 | 3 | B | 5 | D | 0 | 0 | 16 | 16 | 16 |
D | 18 | 5 | 4 | C | 6 | D | 0 | 1 | 18 | | 18 |
D | 25 | 6 | 5 | D | 7 | D | 1 | 1 | | | 18 |
D | 34 | 7 | 6 | D | 8 | A | 1 | 0 | | 34 | 18 |
A | 40 | 8 | 7 | D | | | 0 | 1 | 40 | | 40 |
残りはささいです。 Esによってグループ化し、このテーブルを得るためにEeの最大値を取得します。
Cv | Es | Ee |
----|----|----|
A | 10 | 11 |
B | 14 | 14 |
C | 16 | 16 |
D | 18 | 34 |
A | 40 | |
あなたは、次の開始と効果的な終了時刻を移入自体に再びテーブルに参加したり、それをつかむためにlead()
ウィンドウ関数を使用したい場合。
あなたの要件を詳しく教えてください。変更するたびに列を選択すると、正確に何をしようとしていますか?あなたが望むものの例が役に立つでしょう。 –
ええ、私はより良いタイトルを使用できました。基本的に、私が与えた例の文脈では、私は、歴史のある時点で、あるいは現時点で、人、その国、そしてその部門を見つけたいと思っています。これを行うには、このディメンションが変更される可能性があるため、これらの列が変更されたときにいつでも見つけることができます – simplycoding
指定した編集に対して、 –