SQLでサブクエリを使って除外条件を指定する際に利用される「NOT IN」と「NOT EXISTS」。両者の動作の違いやNULLの扱い、パフォーマンス差を実例付きで徹底解説します。
EXISTS は ANSI SQL(国際標準SQL)に含まれる構文 のため、 ほぼすべてのリレーショナルデータベースで利用できます。 古いバージョンの一部DBを除き、標準構文として移植性が非常に高い のが特徴です。
1. NOT IN と NOT EXISTS の基本構文
句 構文例 説明
NOT IN SELECT * FROM A WHERE ID NOT IN (SELECT ID FROM B); サブクエリの結果に含まれないIDを抽出
NOT EXISTS SELECT * FROM A WHERE NOT EXISTS (SELECT 1 FROM B WHERE A.ID = B.ID); Bに同じIDが存在しない場合のみAを取得
ポイント:
2. 動作の違い(NULLの扱いに注目)
条件 NOT INの結果 NOT EXISTSの結果
サブクエリにNULLが含まれる すべての行が除外される 正常に比較できる
サブクエリが空(0件) 全件取得される 全件取得される
理由: NOT IN は内部的に「A.ID <> B.ID」を繰り返すような処理を行うため、NULLが含まれると比較結果がUNKNOWN となり、全体が評価されなくなる。 一方、NOT EXISTS は行ごとに存在チェックを行う ため、NULLの影響を受けない。
3. 実行結果の比較例
以下の例を見てみましょう。
テーブルA
テーブルB
4. パフォーマンスの違い
比較項目 NOT IN NOT EXISTS
NULLの影響 受ける 受けない
実行計画(最適化) インデックス利用されにくい場合あり 最適化されやすい
大量データ時の効率 遅くなるケースあり より安定して高速
Oracleの最適化傾向 半結合(Anti-Join)に変換されることあり 同様に最適化される
実測例(概略)
件数 NOT IN所要時間 NOT EXISTS所要時間
1万件 0.25秒 0.20秒
10万件 3.1秒 1.8秒
※ 実測環境:Oracle 19c、インデックスあり、CPU 4コア相当
5. どちらを使うべきか
条件 推奨句
サブクエリにNULLが含まれる可能性あり NOT EXISTS
データが小規模でNULLなし どちらでも可
大規模データ・実行計画を重視 NOT EXISTS(推奨)
可読性を優先 NOT EXISTS のほうが誤動作が少ない
6. まとめ
観点 内容
ANSI SQL対応 ○(どのDBでも使用可能)
実行パフォーマンス DBごとに最適化される(MySQL 8以降で特に改善)
推奨度 高い(NOT INより安全で移植性が高い)
注意点 MySQL 5.x 以前では最適化が弱いケースがある
✔ 結論: 除外条件を指定する場合は、基本的に「NOT EXISTS」を使う 方が安全で高速です。 ただし、NULLが確実に存在しないことが保証される小規模データではNOT INも選択肢になります。
SQLを使ってデータを扱うとき、表の形を「横持ち」や「縦持ち」に変換したい場面は多々あります。 例えば、月ごとの売上を列ごとに並べたい、あるいはアンケート結果を1列にまとめたいなど。
こうした「表の回転」に便利なのが PIVOT と UNPIVOT です。 本記事では、それぞれの使い方と、主要なDBMSごとの違いを整理します。
PIVOTとは?
PIVOTは 縦持ちデータを横持ちに変換する 機能です。 例:月ごとの売上を集計して列化する。
サンプルデータ
商品 月 売上
A 1月 100
A 2月 150
B 1月 200
B 2月 180
PIVOTのイメージ
UNPIVOTとは?
UNPIVOTは 横持ちデータを縦持ちに変換する 機能です。 例:上記の「商品×月売上表」を再び「商品・月・売上」の縦持ちに戻す。
各DBMSでの書き方比較
1. SQL Server
SQL Serverはネイティブで PIVOT / UNPIVOT をサポート。
2. Oracle
Oracleは PIVOT / UNPIVOT が標準で利用可能。
3. PostgreSQL
PostgreSQLはPIVOT句を持たないため、crosstab関数(tablefunc拡張) を使う。
4. MySQL
MySQLには PIVOT 句はなく、CASE式 + GROUP BY を使う。
DBMS比較表
DBMS PIVOT対応 UNPIVOT対応 代替手段
SQL Server ネイティブ ネイティブ そのまま使用可
Oracle ネイティブ ネイティブ そのまま使用可
PostgreSQL なし なし crosstab関数 / UNION ALL
MySQL なし なし CASE式 + GROUP BY / UNION ALL
まとめ
「集計を横に展開したい」あるいは「フラットに戻したい」とき、 DBMSに応じた方法を覚えておくと、データ整形がぐっと楽になります。
SQLで全角文字と半角文字を判定するにはLENGTHBやOCTET_LENGTH関数で取得したバイト数とLENGTH関数で取得した文字数を比較することで判断することができます。
使用例
補足
1. 判定ロジックの背景と利用ケース
全角文字・半角文字を判定する目的として、データ品質管理(例:ユーザー登録時の文字種チェック)、システム移行時の文字コード整合性確認、レポートや印刷用データの整形などが考えられます。
特にマルチバイト文字(日本語)環境では、1文字あたりのバイト長が異なるため、バイト長と文字数のずれを利用した判定が有効です。
本記事で紹介されているように、例えば PostgreSQL の LENGTH() と OCTET_LENGTH() の比較を使う方法はシンプルでクロスプラットフォームにも応用可能です。
2. 各データベースでの関数違い・注意点
PostgreSQL:文字数を返す LENGTH(text)、バイト長を返す OCTET_LENGTH(text) を使う。 例:WHERE length(col) <> octet_length(col)
Oracle:Oracle には直接 OCTET_LENGTH がないが、LENGTHB() がバイト長を、LENGTH() が文字数を返す(ただしCHAR型/NCHAR型、CHARSET設定によって挙動が異なる)ので、LENGTHB(col) <> LENGTH(col) という書き方が使えます。
MySQL:CHAR_LENGTH(col) が文字数、LENGTH(col) がバイト数を返すので、CHAR_LENGTH(col) <> LENGTH(col) といった判定が可能。
SQL Server:LEN() が文字数(末尾の空白はカウントされない)、DATALENGTH() がバイト長(文字コード依存)を返すので、環境次第では LEN(col) * 2 <> DATALENGTH(col) などと併用するケースがあります。
注意点として、文字コード(UTF-8、UTF‐16、Shift_JIS 等)が環境によって異なると「半角=1バイト」「全角=2バイト」という前提が崩れる場合があります。特にUTF-8では日本語全角文字が3バイトある場合もありますので、バイト数の比較ロジックを適用する際は対象の文字コードを意識してください。
3. 実運用的な工夫・パフォーマンス面の注意
大規模テーブルでこのようなチェックを行う場合、たとえば WHERE LENGTH(col) <> OCTET_LENGTH(col) のような関数を大量の行に対して実行するとインデックスが効かず、全表スキャンになる可能性があります。
判定だけでなく、誰がいつどのレコードを修正したか(=トレーサビリティ)を残すなら、更新日時・更新者カラムを活用して「チェック済み/要修正」などのフラグを設けると管理しやすくなります。
将来的に正規表現(REGEXP)や文字列関数(例えば Unicode プロパティを利用した判定)を使って「半角カタカナ」「全角英数字」「漢字のみ」などより細かく制御したい場合も多いため、可能ならその準備もしておくと良いでしょう。
4. よくある誤り・ハマりどころ
前提として「文字数とバイト数が異なる=全角含む」という仮定をしているため、 半角カタカナ や 絵文字(マルチバイト4バイトなど) が含まれていると誤検知される可能性があります。たとえば、UTF-8環境で絵文字が4バイトなので「文字数1、バイト数4」→「異なる」と判定されてしまう。
文字コード設定の違い:テーブル/カラムごとに異なる照合順序・文字セットが指定されていると、バイト数や文字数の挙動が予想と異なることがあります。例えば MySQL で utf8mb4 を使っているなら日本語全角文字が3バイトではなく4バイトになるケースあり。
更新系の処理で「半角に変換された/全角に変換された」履歴を残していないため、修正したデータが「元はどちらだったか」が分からなくなるという運用上のリスク。必要に応じて「修正前の値」保持やログ出力を検討するべきです。
5. 具体的な応用例・コードスニペット
PostgreSQL での運用例
MySQL での運用例
Oracle での運用例
6. フォローアップ可能な内容・発展トピック
正規表現を用いて「全角ひらがな」「全角カタカナ」「全角英数字」「半角英数字」などを分類・抽出する方法。
文字種に応じて別テーブルへアーカイブ・除外といったワークフロー設計。
BI/レポート用途で「文字種別カウント」を可視化する方法(例:Excel/Tableau/BIツールを用いた文字種分布グラフ化)。
外部システム(CSV/Excel)からデータインポート時に「文字種チェック+自動整形(全角→半角、半角→全角)」を組み込むETL(Extract-Transform-Load)設計。
将来的に多言語対応を視野に入れた「Unicodeカテゴリ判定」(たとえば、CJK文字・ラテン文字・Emoji など)を含めた文字種チェック。
「駑馬十駕」を信念に IT系情報を中心に調べた事をコツコツ綴っています。