同僚から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=不明、未記載)とします。
| OBS | USUBJID | SEQ | FLG |
|---|---|---|---|
| 1 | A001 | 1 | 0 |
| 2 | A001 | 2 | 99 |
| 3 | A001 | 3 | 1 |
| 4 | A001 | 4 | 1 |
| 5 | A001 | 5 | 99 |
| 6 | A001 | 6 | 1 |
| 7 | A001 | 7 | 1 |
| 8 | A001 | 8 | 0 |
| 9 | A001 | 9 | 99 |
| 10 | A001 | 10 | 99 |
| 11 | A002 | 1 | 0 |
| 12 | A002 | 2 | 0 |
| 13 | A002 | 3 | 0 |
| 14 | A002 | 4 | 1 |
| 15 | A002 | 5 | 1 |
| 16 | A002 | 6 | 0 |
| 17 | A002 | 7 | 0 |
| 18 | A002 | 8 | 0 |
| 19 | A003 | 1 | 0 |
| 20 | A003 | 2 | 99 |
| 21 | A003 | 3 | 1 |
| 22 | A003 | 4 | 1 |
| 23 | A004 | 1 | 1 |
| 24 | A004 | 2 | 99 |
| 25 | A005 | 1 | 0 |
| 26 | A005 | 2 | 0 |
| 27 | A006 | 1 | 0 |
| 28 | A006 | 2 | 0 |
| 29 | A006 | 3 | 0 |
| 30 | A006 | 4 | 99 |
| 31 | A007 | 1 | 0 |
| 32 | A007 | 2 | 99 |
| 33 | A007 | 3 | 1 |
| 34 | A007 | 4 | 99 |
| 35 | A007 | 5 | 0 |
| 36 | A008 | 1 | 1 |
| 37 | A009 | 1 | 99 |
| 38 | A010 | 1 | 0 |
| 39 | A010 | 2 | 1 |
| 40 | A010 | 3 | 1 |
| 41 | A010 | 4 | 99 |
臨床関連のデータだとありがちなデータかと思います。このうち少なくとも一回以上イベントが発生した症例を抽出してみましょう。
クエリは以下の通りとなります。
Copyproc 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」としても同様の結果が得られるでしょう。
Copyproc 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関数を併用すれば抽出できます。
この考えを踏まえたクエリは以下の通りとなります。
Copyproc sql ; select usubjid from raw group by usubjid having min(flg)=0 and max(flg)=0 ; quit;
このようにhaving句を使うと症例毎に特定のデータの存在を調べることができます。この処理自体はSASでもできますが、データを要約する処理はSQLのほうが直感的にわかりやすいと思います。
個人的には頻出テクニックです。





