RDBでは1つの表でさまざなデータを表現しようとすると、表現しきれないデータがあったり、無駄な部分が増えたりするため、複数の表の結合により解決します。表の結合は、FROM句に複数の表を指定すると暗黙的に行われます。そこでまずはFROM句の記述について解説し、その後で表の結合について解説をしていきましょう。
表を指定するFROM句
SQL文では、表の指定をFROM句で行います。SQL文では表の指定が必要になるので、すでにここまでに何度も使ってきました。しかし、ここまで使ってこなかった指定として、複数の表の指定があります。複数の表を指定するには、FROM句に複数の表名を、「,」(カンマ)で区切って指定します。
表A
ナンバー | 名前 |
---|---|
112 | ValueA1 |
123 | ValueA2 |
113 | ValueA3 |
126 | ValueA4 |
131 | ValueA5 |
表B
コード | 名前 |
---|---|
112 | ValueB1 |
131 | ValueB2 |
表名と列名を「.」(ピリオド)で接続して列を指定します。このように記述すると、たくさんの表を指定したSQL文であっても、読み出す列を一意に特定することができます。
名前 |
---|
ValueA1 |
ValueA5 |
別名による表の指定
表の指定には、別名を使うこともできます。これはここで解説した列の別名と同じです。表の名前が長いときや、わかりづらいときに、FROM句で表に別名を指定すると、その別名を列の選択や、WHERE句での指定に使うことができます。
ORACLE MS-SQL
SELECT <別名>.<列名>, ・・・
FROM <表名1 別名1>,<表名1 別名2>, ・・・
MSAccess
SELECT <別名>.<列名>, ・・・
FROM <表名1> AS <別名1>,<表名1> AS <別名2>, ・・・
SELECT A.名前 FROM 表A A, 表B B WHERE A.ナンバー=B.コード;
名前 |
---|
ValueA1 |
ValueA5 |
表の結合について
実際の業務では、複数の表を使ってさまざまな処理を行っています。また、データ処理では、複数の表でさまざまな演算を行い、新たな表を作り出し、さらにその表を組合せてデータ処理を行うこともあります。
こうした、複数の表をつなぎ合わせる処理を結合(JOIN)といいます。結合には次のような種類があります。
- 交差結合
- 自然結合
①内部結合 ②外部結合
交差結合
2つの表を直積により結合することを交差結合またはクロス結合といいます。この結合は最も単純な結合です。
SELECT <列名1> ・・・ FROM <表1> CROSS JOIN <表2>
ORACLE MS-SQL MSAccess
SELECT <列名1> ・・・ FROM <表1> , <表2>
では実際に交差結合の例を見ていきましょう。次の2つの表を交差結合してみます。
表 : 名前リスト
社員番号 | 姓 | 名 | 勤務地コード |
1001 | 山田 | 花子 | 123 |
1022 | 斉藤 | 隆 | 223 |
1056 | 佐藤 | 一郎 | 223 |
表:事業所リスト
事業所コード | 事業所名 |
123 | 品川事業所 |
223 | 赤坂支社 |
301 | 横浜営業所 |
MS-SQL
SELECT * FROM 名前リスト CROSS JOIN 事業所リスト
ORACLE MS-SQL MSAccess
SELECT * FROM 名前リスト, 事業所リスト
実行結果
社員番号 | 姓 | 名 | 勤務地コード | 事業所コード | 事業所名 |
1001 | 山田 | 花子 | 123 | 123 | 品川事業所 |
1022 | 斉藤 | 隆 | 223 | 123 | 品川事業所 |
1056 | 佐藤 | 一郎 | 223 | 123 | 品川事業所 |
1001 | 山田 | 花子 | 123 | 223 | 赤坂支社 |
1022 | 斉藤 | 隆 | 223 | 223 | 赤坂支社 |
1056 | 佐藤 | 一郎 | 223 | 223 | 赤坂支社 |
1001 | 山田 | 花子 | 123 | 301 | 横浜営業所 |
1022 | 斉藤 | 隆 | 223 | 301 | 横浜営業所 |
1056 | 佐藤 | 一郎 | 223 | 301 | 横浜営業所 |
この結果からわかるように、「名前リスト」表と「事業所リスト」表の行同士の組合せがすべて結果として返されています。列はそれぞれの表の列数を足した数に、行はそれぞれの表の行数を掛けた数になります。
自然結合 ①内部結合
自然結合とは、一方の表にある列の値と、もう一方の表のある列の値が同じ行を結び付ける結合です。このような結合を「等結合」と呼ぶこともあります。自然結合は「内部結合」と「外部結合」に分かれます。ここではまず内部結合から解説していきます。
内部結合(INNER JOIN)とは、一方の表の列の値ともう一方の表の列の値が同じ行だけを結合することを言います。内部結合は最もよく使う結合です。ORACLEでは、INNER JOINキーワード、ONキーワードに対応していないため、WHERE句の記述で内部結合を行います。他のRDBMS製品でも、INNER JOINキーワードやONキーワードを使わなくてもWHERE句の記述により内部結合を行うことができます。
書式 MS-SQL MSAccess
SELECT ・・・ FROM <表1> INNER JOIN <表2>
ON <表1>.<列1> = <表2>.<列2>
ORACLE MS-SQL MSAccess
SELECT ・・・ FROM <表1>, <表2>
WHERE <表1>.<列1> = <表2>.<列2>
この記述は、2つの表の間で対応する列を指定する記述です。それぞれで指定された列の値が同じ行が結果として返されます。
交差結合では全ての組合せが返されましたが、内部結合では、その中から条件により抽出されたもにとなります。この結合の条件を「結合条件」と呼びます。
記述自体はWHERE句に使われる通常の条件式と同じなのですが、条件が真になったときに動作が異なります。WHERE句に書かれた通常の条件式の場合には、条件が真になったばあには結果としてその行が返されます。子に対して結合条件が真になると、行に結合が行われます。
実際の使用例を見てみましょう。
表 : 顧客マスタ
顧客コード | 名前 | 電話番号 |
110 | 山田花子 | 0312341234 |
112 | 斉藤隆 | 0451231234 |
115 | 佐藤信也 | 0354325432 |
120 | 森葉子 | 0434444444 |
122 | 小泉加奈子 | 0311221122 |
表:売上リスト
商品コード | 商品名 | 販売価格 | 販売日 | 顧客コード |
1100 | レンズなしカメラ | 1100 | 20010601 | 112 |
1300 | 透明なインク | 180 | 20010601 | 112 |
1100 | レンズなしカメラ | 1200 | 20010602 | 115 |
1201 | 角のない豆腐 | 400 | 20010602 | 120 |
1301 | 開かない蓋 | 480 | 20010603 | 122 |
ここでは、「顧客マスタ」表の「顧客コード」列と「売上リスト」表の「顧客コード」列を内部結合するSQL文を記述してみます。
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ INNER JOIN 売上リスト ON 顧客マスタ.顧客コード=売上リスト.顧客コード;
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ, 売上リスト WHERE 顧客マスタ.顧客コード=売上リスト.顧客コード;
実行結果
名前 | 商品名 | 販売価格 |
斉藤隆 | レンズなしカメラ | 1100 |
斉藤隆 | 透明なインク | 180 |
佐藤信也 | レンズなしカメラ | 1200 |
森葉子 | 角のない豆腐 | 400 |
小泉加奈子 | 開かない蓋 | 480 |
「顧客マスタ」表の「顧客コード」列と「売上リスト」表の「顧客コード」列の値が同じ5行だけが結果として返されました。
また、さらに複数の表を結合することもできます。INNER JOIN句を使う場合には、INNER JOINを繰り返し記述していきます。1つ目の結合の記述を「( )」で囲み、最初に結合が行われるようにし、その結果に対してさらに内部結合するように記述します。WHERE句を使う場合には、FROM句に表名を追加し、WHERE句内に結合条件をすべてAND演算子でつないで記述します。
ここでは、「顧客マスタ」表と「売上リスト」表に、さらに次の「商品マスタ」表を結合してみます。
表:商品マスタ
商品コード | 商品名 | 販売価格 | 販売元 |
1100 | レンズなしカメラ | 1100 | びっくり愛 |
1300 | 透明なインク | 180 | うつけ屋 |
1201 | 角のない豆腐 | 400 | 裸の大様堂 |
1301 | 開かない蓋 | 480 | 非実用INC |
MS-SQL MSAccess
SELECT 顧客マスタ.名前, 売上リスト.商品名, 商品マスタ.商品単価, 売上リスト.販売価格
FROM (顧客マスタ INNER JOIN 売上リスト ON 顧客マスタ.顧客コード=売上リスト.顧客コード)
INNER JOIN 商品マスタ ON 売上リスト.商品コード=商品マスタ.商品コード;
SELECT 顧客マスタ.名前, 売上リスト.商品名, 商品マスタ.商品単価, 売上リスト.販売価格
FROM 顧客マスタ, 売上リスト, 商品マスタ
WHERE 顧客マスタ.顧客コード=売上リスト.顧客コード
AND 売上リスト.商品コード=商品マスタ.商品コード;
実行結果
名前 | 商品名 | 商品単価 | 販売価格 |
斉藤隆 | レンズなしカメラ | 1200 | 1100 |
斉藤隆 | 透明なインク | 180 | 180 |
佐藤信也 | レンズなしカメラ | 1200 | 1200 |
森葉子 | 角のない豆腐 | 400 | 400 |
小泉加奈子 | 開かない蓋 | 500 | 480 |
なお、INNER JOINキーワードやONキーワードが使えるRDBMS製品を使っている場合には、WHERE句に結合条件を記述するよりも、これらのキーワードを使ったほうが良いでしょう。それは、表と表の結合関係がひと目でわかるだけでなく、通常の条件式との区別も容易になるからです。
また、一部のRDBMS製品では、INNER JOINキーワードの「INNER」を省略することもできます。そのため次のような記述も可能です。
FROM 顧客マスタ JOIN 売上リスト ON 顧客マスタ.顧客コード=売上リスト.顧客コード;
省略はすべての製品で可能な訳ではないので、マニュアルなどで仕様を確認してください。
内部結合と検索条件の指定
内部結合を行った結果に対して、さらに検索条件を設定することもできます。このとき結合は、WHERE句で検索条件の評価を行ううよりも先に実行されます。まずは結合し、その結合で作成された表に対して条件判断が行われることになります。
検索条件の指定はWHERE句に記述します。INNER JOINキーワードを使ていない場合はWHERE句にAND演算子を使って記述します。
MS-SQL MSAccess
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ INNER JOIN 売上リスト ON 顧客マスタ.顧客コード=売上リスト.顧客コード
WHERE 売上リスト.販売日='20010602';
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ, 売上リスト
WHERE 顧客マスタ.顧客コード=売上リスト.顧客コード AND 売上リスト.販売日='20010602';
実行結果
名前 | 商品名 | 販売価格 |
佐藤信也 | レンズなしカメラ | 1200 |
森葉子 | 角のない豆腐 | 400 |
先ほどの内部結合に抽出された5件から、さらに販売日により条件を絞り込んでいるので、2件だけが結果として返されました。
自然結合 ②外部結合
内部結合では、複数の表の間で対応する列の値が等しいものだけが結合され、結果として返されました。そのため、どちらかにしか存在しない値をもつ行は結合されませんでした。しかし、処理の目的によってはある表だけは必ず残したいという場合があります。こういった場合に用いるのが外部結合(OUTER JOIN)です。外部結合には、結合の方向により2つの種類があります。
- 左外部結合
- 右外部結合
なぜ、左や右という名前なのかというと、SQL文で先に記述した表名とSQL文で後に記述した表に指定した表のどちらを優先させるかを考えるからです。
左外部結合
左外部結合は、先に記述した左側の表を優先し、左側の表に関してはすべての行を残す結合です。
書式
MS-SQL MSAccess
SELECT ・・・ FROM <表1> LEFT OUTER JOIN <表2> ON <表1>.<列1> = <表2>.<列2>
ORACLE
SELECT ・・・ FROM <表1>, <表2> WHERE <表1>.<列1> = <表2>.<列2>(+)
MS-SQL
SELECT ・・・ FROM <表1>, <表2> WHERE <表1>.<列1> *= <表2>.<列2>
外部結合の書式にはたくさんの種類があります。
ORACLEではWHERE句の結合条件の中で、優先しない表の列名の後に「(+)」という記号を指定します。
MS-SQLでは LEFT OUTER JOIN キーワードによる記述の他に、WHERE句に記述された結合条件の記述に「*=」を使って指定することができます。
では、左外部結合で結合されなかった行はどのような扱いになるのでしょうか?結合されなかったからといって、返される結果にはそこに値がない訳ではありません。通常その部分にはNULLが格納されています。
MS-SQL MSAccess
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ LEFT OUTER JOIN 売上リスト
ON 顧客マスタ.顧客コード=売上リスト.顧客コード;
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ, 売上リスト
WHERE 顧客マスタ.顧客コード=売上リスト.顧客コード(+);
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ, 売上リスト
WHERE 顧客マスタ.顧客コード *= 売上リスト.顧客コード;
実行結果
名前 | 商品名 | 販売価格 |
山田花子 | ||
斉藤隆 | レンズなしカメラ | 1100 |
斉藤隆 | 透明なインク | 180 |
佐藤信也 | レンズなしカメラ | 1200 |
森葉子 | 角のない豆腐 | 400 |
小泉加奈子 | 開かない蓋 | 480 |
表:売上リスト
商品コード | 商品名 | 販売価格 | 販売日 | 顧客コード |
1100 | レンズなしカメラ | 1100 | 20010601 | 112 |
1300 | 透明なインク | 180 | 20010601 | 112 |
1100 | レンズなしカメラ | 1200 | 20010602 | 115 |
1201 | 角のない豆腐 | 400 | 20010602 | 120 |
1301 | 開かない蓋 | 480 | 20010603 | 122 |
表 : 顧客マスタ
顧客コード | 名前 | 電話番号 |
110 | 山田花子 | 0312341234 |
112 | 斉藤隆 | 0451231234 |
115 | 佐藤信也 | 0354325432 |
120 | 森葉子 | 0434444444 |
122 | 小泉加奈子 | 0311221122 |
結果を見るとわかるように、先に記述されている「顧客マスタ」表(左側)の行がすべて出力されています。そのため「山田花子」の行は商品名、販売価格の値としてNULLが入力されています。ORACLEではNULLを表示しないので、空白で出力されています。
右外部結合
右外部結合は、左外部結合とは反対に、後で指定した右側の表を優先し、右側の表に関してはすべての行を残す結合です。
書式
MS-SQL MSAccess
SELECT ・・・ FROM <表1> RIGHT OUTER JOIN <表2> ON <表1>.<列1> = <表2>.<列2>
ORACLE
SELECT ・・・ FROM <表1>, <表2> WHERE <表1>.<列1>(+) = <表2>.<列2>
MS-SQL
SELECT ・・・ FROM <表1>, <表2> WHERE <表1>.<列1> =* <表2>.<列2>
MS-SQL MSAccess
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ RIGHT OUTER JOIN 売上リスト
ON 顧客マスタ.顧客コード=売上リスト.顧客コード;
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ, 売上リスト
WHERE 顧客マスタ.顧客コード(+) = 売上リスト.顧客コード;
SELECT 顧客マスタ.名前, 売上リスト.商品名, 売上リスト.販売価格
FROM 顧客マスタ, 売上リスト
WHERE 顧客マスタ.顧客コード =* 売上リスト.顧客コード;
実行結果
名前 | 商品名 | 販売価格 |
斉藤隆 | レンズなしカメラ | 1100 |
斉藤隆 | 透明なインク | 180 |
佐藤信也 | レンズなしカメラ | 1200 |
森葉子 | 角のない豆腐 | 400 |
小泉加奈子 | 開かない蓋 | 480 |
この表では、名前が左側の表からの列になります。ここではNULLになっている行はありません。なぜなら「売上リスト」表が右側になっているため、この表の中に顧客名が入っていないことはないためです。