PR
Q 質問 社員テーブルと所属テーブルにアクセスするSELECT文を作成しましたが,正しい結果が検索できなかったり,重複した結果を取り出したりします。どこが間違っているのでしょうか。

A 回答 SELECT文の結果は,検索条件やジョインの種類によって変わってきます。


図1●テーブルの内容と誤ったSELECT文の例
社員テーブルと所属テーブルの2つのテーブルがある。この2つのテーブルに対して図中の3つのSELECT文を作成したが,(1)と(2)は所望の結果は得られず,(3)の結果は同じ内容のレコードが複数返ってきてしまう
 リレーショナル・データベース管理システム(RDBMS)からデータを検索するSQLのSELECT文は,WHERE句の検索条件や,ジョインの種類によって取り出す結果が変わります。図1[拡大表示]に示したSELECT文には文法的な間違いは無いのですが,完全に整合性が取られていないデータの場合,目的の結果を得ることができません。

ジョインの種類を使い分けよう

 図1(1)[拡大表示]は「全社員の名前と所属名称を検索する」ために作成したSELECT文ですが,このSELECT文では所属IDが「2」や「NULL」の社員は検索できません。全社員が検索できないため,誤ったSELECT文になっています。

 この例で注意しなければならないのは,結合カラムになっている所属IDカラムの値です。社員テーブルの所属IDカラムには「1,2,3,NULL」の4つの値がありますが,所属テーブルの所属IDカラムは「1,3,4」の3つの値です。社員テーブルにある「2」や「NULL」は所属テーブルになく,所属テーブルにある「4」は社員テーブルにありません。このようなテーブルの場合,ジョインの種類によって検索結果は変わってしまいます。ここで言っているジョインの種類とは,インナー・ジョイン(内部結合)やアウター・ジョイン(外部結合)のことです。

図2●インナー・ジョインとアウター・ジョインの動き
インナー・ジョイン(内部結合)は結合カラムが一致するレコードだけを結合し,アウター・ジョイン(外部結合)は結合カラムが一致しないレコードも結合対象とする
 図1(1)のSELECT文の場合,ジョインの種類を指定していないため暗黙的にインナー・ジョインが選択されます。そのため,所属先が所属テーブルにないレコードが検索できなかったのです。インナー・ジョインは,ジョイン対象テーブルの結合カラムが同じレコードだけを検索結果に含めるためです(図2左[拡大表示])。

 所属先が見つからなくても社員が検索されるようにするには,インナー・ジョインではなく,アウター・ジョインを指定する必要があります(同右[拡大表示])。アウター・ジョインを使えば,結合カラムが同じにならなくても検索結果に含めることができます。この例では,社員テーブルの全レコードを検索結果に含むようにしたいため,

SELECT 社員.名前,所属.名称
FROM 社員 LEFT OUTER JOIN 所属
ON 社員.所属ID = 所属.所属ID

と記述します。FROM句の2つのテーブル名の間に「LEFT OUTER JOIN」と記述すると,左アウター・ジョインとなり,左側(社員)テーブルの全レコードを含む結果になります。「LEFT JOIN」と記述するRDBMSもあります。ちなみに,「RIGHT OUTER JOIN」または「RIGHT JOIN」とすると,右側(所属)テーブルの全レコードを含む結果になります。

NULLの扱いには注意が必要

 図1(2)[拡大表示]は「所属先が『総務』以外の社員名を検索する」ために作成したSELECT文です。社員テーブルの所属IDが「1」ではない社員を検索しなければなりませんが,このSELECT文では所属IDが「NULL」の社員は検索できません。

 例のように検索条件に「社員.所属ID <> 1」とした場合,社員テーブルの所属IDカラムが「1」ではない社員を検索しますが,その際「NULL」のレコードは検索されません。NULLのカラムを持つレコードを検索するには,「ISNULL」という条件を指定する必要があります。今回の例の場合,

SELECT 社員.名前 FROM 社員
WHERE 社員.所属ID <> 1
 OR 社員.所属ID IS NULL

とすれば,目的の結果を検索することができます。

 図1(3)[拡大表示]は,「1990年以降に入社した社員が所属する部署名を検索する」ために作成したSELECT文です。おそらく「人事」という結果を期待していると思われますが,このSELECT文では「人事」「人事」となってしまいます。2つの同じ結果が出力されるのです。

 図1(3)は文法的にも意味的にも正しいのですが,検索結果のレコード数に対する配慮が足りません。1990年以降に入社した社員は4人で,そのうち所属先が明らかなのは2人です。どちらも「人事」に所属しているため,検索結果は2レコード出力されます。このように同じ結果を重複して出力しないようにするには,「DISTINCT」というキーワードを付けます。具体的には,

SELECT DISTINCT 所属.名称 FROM
社員,所属 WHERE 社員.所属ID = 所属.
所属ID AND 社員.入社年度 >= 1990

とすれば目的の結果を取り出すことができます。

(本誌)