【SQL】IN句とは/EXISTS句とは(基本と頻出利用方法、違い、否定形まで) | 若手エンジニアのブログ

若手エンジニアのブログ

文系出身の若手女子エンジニアによる技術ブログ。
日々の経験や学びをアウトプットするためにブログを書いています。
バックエンド(Java+SpringFramework)を経てインフラエンジニアになりました。
今は育休中につき、本で勉強したことを中心にアウトプットしています。

今月から仕事内容が変わり、本格的にSQLを使うことになりました。

SQLは情報処理試験で勉強したものの、業務でしっかり使うのは初めての経験。

ので、しばらくは、知らなかったSQLを1つずつまとめていきたいと思います。

 

今日はIN句とEXISTS句について。

 

※Oracle Database19cで確認しています。

データベースによって異なる部分などあるかもしれないため、自己責任でお読みください。

 

もくじ

1.IN句役割と基本の使用方法応用編

2.EXISTS句役割と基本の使用方法応用編

3.IN句とEXISTS句の違い

4.IN句とEXISTS句の否定形NOT INNOT EXISTSNOT INの注意点

 

1.IN句

■役割と基本の使用方法

「OR(または)」の条件を、よりコンパクトにわかりやすく記述できるようにするための句。

 

例えば、列Aの値が「1」または「2」である場合のみ、レコードを取り出す、

といった処理はよくあると思う。

 

この処理を実現するためのコードとして、以下のような書き方が可能ではある。

 SELECT * 

 FROM TEST_TABLE

 WHERE COLUMN_A = 1 OR COLUMN_A = 2;

 

が、「COLUMN_A」という列名を、「COLUMN_B」という列名に変えたいとき、

わざわざ2か所も変えないといけない手間が生じる。

 

また、「1」か「2」だけでなく「3」であるときもレコードを取り出したいとなると、

「OR COLUMN_A = 3」という同じようなコードをさらに追加する必要があり、どうしても冗長となってしまう。

 

一方、IN句を利用すると、同じ意味のコードを以下のようにまとめて書くことが可能である。

 SELECT *

 FROM TEST_TABLE

 WHERE COLUMN_A IN (1, 2);

 

WHERE句内に、列名 IN (OR条件とする値(カンマ区切り)) を書くこととなるため、

列名を何度も書く必要がなくなり、コードの可読性も上がる。

もちろん、「3」の条件を加えたいときも、「IN (1, 2, 3)」のようにすればOKである。

 

■応用編

IN句で指定できるのは、カラムに格納された値そのもの(上の例でいえば、「1」や「2」という値)だけではない。

以下のようなSQLの入れ子での利用も可能である。

 SELECT * 

 FROM TEST_TABLE 

 WHERE COLUMN_A IN (

  SELECT ID 

  FROM ID_TABLE

 );

 

この場合、まずはIN句内(内側)のSELECT文が実行され、IDテーブルの各ID値が取得される。

取得された各ID値がIN句に代入され、外側のSELECT文が実行されることとなる。

 

私はまだ実務で入れ子式のコードにはお目にかかっていないが、

ググるとけっこう出てくるので、よくある使い方なんだと思う。

 

 

2.EXISTS句

■役割と基本の使用方法

EXISTS句は、「SQLの実行結果が存在するかどうか」を条件としたいときに利用する。

存在する場合(TRUEの場合)のみ、SQLを実行することになる。

 

例えば、以下のように記述する。

 SELECT * 

 FROM TEST_TABLE 

 WHERE EXISTS( 

  SELECT * 

  FROM ID_TABLE 

  WHERE ID = 1 

 );

 

EXISTS句でもIN句と同様、まずはEXISTS句内(内側)のSELECT文が実行される。

上記例では、IDが1であるレコードを、IDテーブルから取得するというSQLが先に実行される。

 

EXISTS区内のSQLで、結果が1つ以上返された場合(=EXISTつまり存在がTRUEの場合)は、

外側のSQLである、「SELECT * FROM COFFEE」部分が実行される。

結果が1つもなかった場合(=EXISTつまり存在がFALSEの場合)は、外側のSQLは実行されない。

 

■応用編

EXISTSは、外部結合を併用することができる。

 

例えば…

 SELECT * 

 FROM TEST_TABLE 

 WHERE EXISTS( 

   SELECT * 

   FROM ID_TABLE 

   WHERE TEST_TABLE.COLUMN_A = ID_TABLE.ID

 );

 

通常、外部結合するには、JOIN句などで結合を記述する必要があるが、

EXISTS句内ではJOIN句を記述せず、上の例のようにすれば外部結合してくれる。

 

ちなみに外部結合が出てきたとしても、

「EXISTS句内を先に確認し、存在有無で外側のSQLを実行するかどうか決める」流れは基本通りである。

 

3.IN句とEXISTS句の違い

IN句はCOLUMN_A列の値が「1か2」であるレコードを抽出し、当該レコードに対して処理をする。

EXISTS句はCOLUMN_A列の値が「1か2であるレコードが存在する場合」、処理を実行する。(EXISTS句によるレコードの抽出はない)

 

4.IN句とEXISTS句の否定形

IN句、EXISTS句とも、それぞれ否定形が存在する。

構文自体はどちらもいたってシンプルで、それぞれ「NOT IN~」、「NOT EXISIS~」と、頭に「NOT」をつけてやればOKである。

 

■NOT IN

IN句内に記述した各値のいずれでもないレコードに対し、外側のSQLを実行することになる。

 

例えば以下のようなSQLであれば、COLUMN_Aの値が1でも2でもないレコード(3とか)に対して処理を実行する。

 SELECT * 

 FROM TEST_TABLE 

 WHERE COLUMN_A NOT IN( 1, 2 );

 

■NOT EXISTS

EXISTS句内のSQLを実行した結果、該当するレコードがなかった場合のみ、外側のSQLを実行することになる。

 

例えば次のSQLなら、IDテーブルにID列の値が1であるレコードがなかった時に、

TESTテーブルの各レコードを取得する処理となる。

ID列の値が1のレコードがあった場合は、何もしない。

 SELECT * 

 FROM TEST_TABLE 

 WHERE NOT EXISTS( 

  SELECT * 

  FROM ID_TABLE 

  WHERE ID = 1 

 );

 

■NOT INの注意点

NOT INで気をつけねばならないことがある。

それは、IN句の選択肢の中にNULLがある場合、想定した処理結果にならないということである。

 

例えば以下のSQLがあり、ID_TABLEには、ID=5となるレコードがない(NULL)とする。

 SELECT * 

 FROM TEST_TABLE 

 WHERE COLUMN_A NOT IN (

  SELECT ID 

  FROM ID_TABLE

  WHERE ID = 4 OR ID = 5

 );

 

1つでもNULLになるものをIN句内で含むと、他にいくつ条件を書いていても、IN句は何も結果を返してくれない(結果的に、IN句が無かったかのようになる)。

 

従って、今回であれば4と5をIN句内に含んでいるため、

本来ならCOLUMN_Aが4でも5でもないレコードをSELECTしてくるはずが、

COLUMN_Aが4や5のレコードもSELECTされてしまう。

 

意図しない処理結果になることを防ぐには、

IN句内のWHERE条件に、「IS NOT NULL」(NULLでない場合にレコードを取得する)を追加する必要がある。

 

 

ちなみにNOT INは、処理が重くなるという情報がちらほらとネットにあった。

実際どれくらい重くなるのか(ないし、気にするほどでもないのか)は環境にも依存すると思われるが、

NOT INは上記のようなNULL問題もあり、積極的に使うようなものではないのかもしれない。
 

 

 

今回は以上!