SQL Procedureを使ったデータセットの結合について紹介します。
使用するサンプルデータ
AとBの2つのデータセットを使います。
それぞれ4レコードずつですが、ID番号3と4は重複していることに注意してください。
また、Aは2018年に、Bは2019年に記録されたデータを想定します。
サンプルデータA, B
data A;
input id sex $ age;
datalines;
1 M 20
2 F 30
3 M 40
4 F 50
run;
data B;
input id sex $ age;
datalines;
3 M 40
4 F 50
5 F 60
6 F 70
run;
title "A (year=2018)";
proc print data=A; run;
title "B (year=2019)";
proc print data=B; run;
title;
実行結果です。
列(縦)方向の結合(Combining)
列方向の結合はUNION
句を使います。
UNION
UNION
を使ってデータセット同士を列方向に結合することができます。
構文はデータセットとデータセットをUNIONで繋いであげるだけです。
UNION
/* データセット UNION データセット */
select * from A union select * from B;
ただし、UNION
を使う際に注意しないといけないのは、重複したレコードは結合時に除外されてしまうことです。
重複したレコードを削除せずに結合したい場合は、UNION ALL
とすればOKです。
UNION ALL
select * from A union select * from B;
select * from A union all select * from B;
UNIONの仕組みは非常にシンプルで、指定されたデータセット同士をそのまま縦方向に結合します。
UNION以外は通常のSELECT文の使い方と何ら変わりなく使えます。
例
/* AはID=1だけを結合 */
select * from A where id=1 union select * from B;
/* どのデータセットから追加したかわかるようにラベルを追加 */
select "A" as dsn, 2018 as year, * from A union
select "B" as dsn, 2019 as year, * from B;
UNIONのエラーチェックは、列ごとにデータ型が一致しているかどうか程度です。
そのため、結合するデータセットの列の数が同じであるか、列順が同じであるかは自身で保証する必要があります。
このことは次のコードで確認できます。
エラーとなる例
/* AとSASHELP.CLASSを結合しようすると、次のようなエラーが返る */
select * from A union all select * from SASHELP.CLASS;
/* ERROR: 列1 (UNIONの第1コントリビュータからの列)は第2コントリビュータからの列とはタイプが異なります。 */
/* 列タイプを揃えるとWARNINGが返るものの実行はできる。 */
select sex,age from A union all select sex,height,weight from SASHELP.CLASS;
/* WARNING: UNION ALLセットの実行により、テーブルをNULL列で拡張しました。 */
1列目について、どちらも文字なのでUNIONは実行されます。
ちなみに、ラベルなど列に関する情報は、列名が一致先に指定したデータセットの情報が優先されます。
ここではAのsexはCHAR(8)、CLASSのsexはCHAR(4)ですが結合後はCHAR(8)となります。
2列目について、AのageとCLASSのheightを結合しようとしています。
実際には全く意味の違う列ですが、データ型が一致しているのでUNIONは実行されます。
逆にいうと、列の位置が違っていたとしても、データ型が一致する限りエラーにならないため注意する必要があります。
3列目は、Aには存在しない列との結合です。
一方が存在しない場合には、存在している列にもとづいて自動的に空白列が生成されます。
行(横)方向の結合(Joining)
列方向の結合はJOIN
句を使います。
Left join
Left join
を使った結合は次のように書きます。
Left join
select * from A /* Aに対して、 */
left join B /* Bを左から結合 */
on A.id= B.id /* AのIDとBのIDが一致するかを判別 */
;
/* 重複する列を指定する場合は「データセット.列名」で指定 */
select
A.id,
A.sex as A_sex, /* 同じ名前の列は「as」で別名を指定 */
B.sex as B_sex,
B.age
from A
left join B
on A.id= B.id
;
Right join
Right join
select * from A right join B on A.id= B.id;
/* RightをLeftにしてAとBも入れ替えれば、結果は同じ(列の順番は違う) */
select * from B left join A on A.id= B.id;
Inner join
Inner join
select * from A inner join B on A.id= B.id;
/* 次のコードでも同じ結果 */
select * from A, B where A.id= B.id;
Full join
Full join
select * from A full join B on A.id= B.id;
直積
Inner joinで出てきた直積です。実務で使うことはないですが参考までに。
直積
select * from A, B;