Hash Object VS SQL
前回に引き続きSASのHash Objectについて紹介します。
FULL Join
- 目的
前回に引き続きFIND()メソッドを使用してprimaryデータセットに対してsecondaryデータセットのオブザベーションを追加更新します。
FULL JOINなのでSecondaryのデータセットに対してもFINDメソッドでprimaryデータセットを追加更新しますが、今回は加えてFIND_NEXTメソッドを使用します。
- FIND_NEXTメソッド
FINDメソッドはルックアップ先のデータセット(下の例ではsecondary)に重複キーが存在する場合、最初の1オブザベーションのデータのみ追加します。そのため下の例のように重複キーが存在するsecondaryデータセットを一意のキーのみ存在するprimaryデータセットで検索して更新する場合、重複キー内のデータを各々検索更新する必要があります。これを実装するのがFIND_NEXTメソッドです。
b.find_next()
- ルックアップ方法
secondaryデータセットに対してもFINDメソッドでprimaryデータセットの内容で更新することをそのまま実装するとsecondaryデータセットもdatasetタグでハッシュオブジェクトを宣言することになりますが、これを実装すると余計なデータセットの入力が増えてしまうためパフォーマンスに影響を及ぼすのと、コードが見づらくなります。
そのため、secondaryデータセットに対しては空のハッシュオブジェクトを宣言し、primaryデータセットに対してルックアップを行ったタイミングでsecondaryデータセットの内容を空のハッシュオブジェクトに追加してあげると余計なデータセットの入力が抑えられ、コードもまとまります。
declare hash x();*1
x.definekey("key");
x.definedone();
.
.
.
set primary end=eof;
x.replace();
上のreplaceメソッドはprimaryデータセットの内容をハッシュオブジェクトxにキーのみ追加しています。後のsecondaryデータセットとのルックアップにてprimaryデータセットとキーを照合するために使用します。
replaceメソッドの次のコードではprimaryデータセットとsecondaryデータセットとの照合でキーが見つからなかった場合データを欠損値にします。
if b.find() ne 0 then
call missing(bdata);*2
output;
最後にprimaryデータセットとsecondaryデータセットのルックアップを実行します。
上のsetステートメントでprimaryデータセットを読み込んだキーのデータをfind_nextメソッドでルックアップしています。
do while(b.find_next()=0);*3;
output;
end;
残ったsecondaryデータセットとprimaryデータセットのルックアップですが、こちらはiteratorを使用してルックアップを実行します。
iteratorとはHash object単体でループとして使用できるオブジェクトになり、while文でiteratorのnext()がtrueの間のループでHash objectxとルックアップを行っています。
declare hiter IB("b");*1;
.
.
.
do while(ib.next()=0);
if x.check() ne 0 then
output;
end;
例:Hash ObjectでのFULL JOIN
data primary;
input key adata;
cards;
1 1
2 2
3 3
4 4
5 5
6 6
7 7
;
run;
data secondary;
input key bdata;
cards;
1 11
1 12
1.5 1.51
3 31
4 4
6 61
6 62
6 63
6.5 6.51
6.5 6.52
7 7
;
run;
data c;
if 0 then
set secondary;
declare hash b(dataset:"secondary", multidata:"y");
declare hiter IB("b");*1;
b.definekey("key");
b.definedata("key", "bdata");
b.definedone();
declare hash x();*1
x.definekey("key");
x.definedone();
do until(eof);
set primary end=eof;
x.replace();*1
if b.find() ne 0 then
call missing(bdata);*2
output;
do while(b.find_next()=0);*3;
output;
end;
end;
call missing(adata);
do while(ib.next()=0);
if x.check() ne 0 then
output;
end;
stop;
run;