proc sql

Proc sqlを極める(13) ーHAVING句ー

同僚からhavingは紹介しないの?と言われたのでSQLの記事を追加しました。havingは結構使うので利用例をまとめました。

HAVING句

HAVING句はWHERE句と同様レコードを抽出するステートメントですが、WHERE句はGROUP BYより前に実行されるのに対し
HAVINGはGROUP BYよりも後に実行されます。通常GROUP BYでグルーピングした後にグループごとに特定の条件に合致するレコードを抽出するのに使います。

注意点としてはHAVINGはGROUP BYと併用しなければならないというわけではなく、単独で使用可能です。GROUP BYを使用しない場合はWHERE句が実行された後のテーブル全体に対して実行されます。

活用例

少なくとも1つ以上のレコードで「有」と判定された症例を抽出する

以下のようなデータがあります。USUBJIDは症例番号、SEQは症例内連番、FLGは特定のイベント発生を識別する変数(1=有、2=無、99=不明、未記載)とします。

OBSUSUBJIDSEQFLG
1A00110
2A001299
3A00131
4A00141
5A001599
6A00161
7A00171
8A00180
9A001999
10A0011099
11A00210
12A00220
13A00230
14A00241
15A00251
16A00260
17A00270
18A00280
19A00310
20A003299
21A00331
22A00341
23A00411
24A004299
25A00510
26A00520
27A00610
28A00620
29A00630
30A006499
31A00710
32A007299
33A00731
34A007499
35A00750
36A00811
37A009199
38A01010
39A01021
40A01031
41A010499

臨床関連のデータだとありがちなデータかと思います。このうち少なくとも一回以上イベントが発生した症例を抽出してみましょう。

クエリは以下の通りとなります。

Copy
proc sql ; select usubjid from raw group by usubjid having sum(case when flg=1 then 1 else 0 end)>=1 ; quit;

まずgroup byで症例毎のサブグループを作成します。havingはこのサブグループに対して抽出を実行します。
case式でflg=1の場合1、それ以外は0を返すようにし、それをサブグループごとに合計します。もしサブグループ内で1つでもflg=1のレコードが存在する場合、
この合計値は1以上となるはずです。これをhaving句で抽出条件として指定すると、flg=1のレコードが存在する症例のみ出力されます。

実行結果は以下の通りです。

USUBJID
A001
A002
A003
A004
A007
A008
A010

イベント発生について不明未記載が存在しない症例を抽出する

イベントが発生が不明未記載のレコードが存在しない症例を抽出してみます。すなわち、flg=99のレコードが存在しない症例を抽出します。
不明未記載は解析において厄介なことになりやすく、別途処理が必要になるケースもあると思います。

不明未記載の存在の有無を症例単位で判定することは頻出問題といえるでしょう。

先ほどと同じ方法で抽出できます。case式でflg=99の場合は1、それ以外は0を返すようにし、これを合計します。
flg=99が存在しなければこの合計値は0になるはずです。

不明未記載のコードが99と最も大きい数値なので、having句の抽出条件を「max(flg)^=99」としても同様の結果が得られるでしょう。

Copy
proc sql ; select usubjid from raw group by usubjid having sum(case when flg=99 then 1 else 0 end)=0 ; quit;

イベントが発生していない症例を抽出する

イベントが発生していない症例を抽出してみます。すなわち、flg=0のレコードのみ存在する症例を抽出します。

flg=0のレコードのみ存在するということは、グループごとに要約した場合最大値および最小値がともに0ということと同義です。
したがってmax関数とmin関数を併用すれば抽出できます。

この考えを踏まえたクエリは以下の通りとなります。

Copy
proc sql ; select usubjid from raw group by usubjid having min(flg)=0 and max(flg)=0 ; quit;

このようにhaving句を使うと症例毎に特定のデータの存在を調べることができます。この処理自体はSASでもできますが、データを要約する処理はSQLのほうが直感的にわかりやすいと思います。
個人的には頻出テクニックです。