【SQL】SELECT文でデータを抽出する方法|実行順序・WHERE条件・NULL・DISTINCT・実践パターンまで解説

【SQL】SELECT文でデータを抽出する方法|実行順序・WHERE条件・NULL・DISTINCT・実践パターンまで解説 SQL

SQL でデータベースからデータを取り出す操作はすべて SELECT 文で行います。シンプルな全件取得から、条件を組み合わせた複雑な抽出まで、SELECT は最も頻繁に使う SQL 文です。

この記事では SELECT 文の基本構文・列の選択方法・WHERE による絞り込みを丁寧に解説するとともに、SQL がどの順番で処理されるか(実行順序)という重要なポイントを図解します。「なぜ WHERE 句で SELECT のエイリアスが使えないのか」など、初学者がつまずきやすい理由もここで理解できます。

スポンサーリンク

SELECT 文の基本構文

SELECT 文の完全な構文は以下の通りです。[ ] で囲まれた部分は省略可能です。

SELECT 文の完全構文
SELECT [DISTINCT] 列1, 列2, ...   -- 取得する列を指定
FROM   テーブル名                  -- データ元のテーブル
[JOIN  別テーブル ON 結合条件]     -- 複数テーブルの結合(省略可)
[WHERE 絞り込み条件]               -- 行の絞り込み(省略可)
[GROUP BY グループ化する列]        -- グループ集計(省略可)
[HAVING グループへの条件]          -- 集計後の絞り込み(省略可)
[ORDER BY ソート列 ASC|DESC]       -- 並び替え(省略可)
[LIMIT n OFFSET m];               -- 取得件数・開始位置(省略可)
サンプルデータ(以降の例で使用)
-- サンプルテーブル: employees(従業員テーブル)
-- id | name       | department | salary | hire_date  | manager_id
-- ---+------------+------------+--------+------------+-----------
--  1 | 田中 太郎  | 営業       |  55000 | 2020-04-01 |       NULL
--  2 | 鈴木 花子  | 開発       |  70000 | 2019-10-01 |          1
--  3 | 佐藤 一郎  | 営業       |  48000 | 2021-07-01 |          1
--  4 | 高橋 美咲  | 開発       |  80000 | 2018-01-15 |       NULL
--  5 | 伊藤 健二  | 人事       |  52000 | 2022-01-01 |          4
--  6 | 渡辺 さくら| 開発       |  NULL  | 2023-04-01 |          4

SELECT で指定できるもの

SELECT の後には列名だけでなく、式・計算・関数・定数・サブクエリなどを指定できます。

全列・特定列・計算式・定数・関数
-- ① 全列を取得(アスタリスク)
SELECT * FROM employees;

-- ② 特定の列を指定
SELECT name, department, salary FROM employees;

-- ③ 計算式(salary に 1.1 を掛けて 10% 増しの給与を計算)
SELECT name, salary, salary * 1.1 AS salary_after_raise
FROM employees;

-- ④ 文字列の結合
-- MySQL / PostgreSQL:
SELECT CONCAT(name, ' (', department, ')') AS name_dept FROM employees;
-- Oracle / SQL Server:
-- SELECT name || ' (' || department || ')' AS name_dept FROM employees;

-- ⑤ 関数の使用
SELECT name, UPPER(department) AS dept_upper,
       LENGTH(name)            AS name_len
FROM employees;

-- ⑥ 定数(全行に同じ値を付与)
SELECT name, 'active' AS status FROM employees;

-- ⑦ スカラーサブクエリ(全従業員の平均給与を各行に付与)
SELECT name, salary,
       (SELECT AVG(salary) FROM employees) AS avg_salary
FROM employees;
SELECT * は本番コードで避けるべき理由:
SELECT * は手軽ですが、①テーブルに列が追加されると意図しない列が返るようになる、②必要以上のデータを転送してネットワーク・メモリを無駄にする、③どの列を使っているか明示されないためコードの可読性・保守性が落ちる、という問題があります。本番の SQL では必要な列を明示して指定してください。

SQL の実行順序(書く順番 ≠ 処理される順番)

SQL は書いた順番とは異なる順序で処理されます。これを理解していないと「なぜエラーになるのか」が分からず詰まってしまいます。

処理順 何をするか
FROM / JOIN どのテーブルからデータを取るかを決める。結合も行う
WHERE 個々の行を条件で絞り込む(集計前)
GROUP BY 指定した列でグループ化する
HAVING グループに対して条件で絞り込む(集計後)
SELECT 取得する列・式を計算してエイリアス名を付ける
DISTINCT 重複行を除去する
ORDER BY 結果を並び替える
LIMIT / OFFSET 取得件数を制限する
実行順序を知るとエラーの理由が分かる:

  • WHERE でエイリアスが使えない理由: WHERE(②)は SELECT(⑤)より先に処理されるため、エイリアス名がまだ確定していない
  • ORDER BY ではエイリアスが使える理由: ORDER BY(⑦)は SELECT(⑤)より後に処理されるため、エイリアス名を参照できる
  • WHERE で集計関数(COUNT, SUM 等)が使えない理由: 集計はGROUP BY(③)以降で行われ、WHERE(②)はその前だから
  • 集計後の絞り込みは HAVING を使う理由: HAVING(④)は GROUP BY(③)の後のため
実行順序に起因する典型的なエラー
-- NG: WHERE でエイリアスを参照しようとするとエラー
-- (WHERE はエイリアスが確定する SELECT より先に処理される)
SELECT salary * 1.1 AS new_salary
FROM employees
WHERE new_salary > 60000;  -- ERROR: unknown column 'new_salary'

-- OK: 元の式を WHERE に書く
SELECT salary * 1.1 AS new_salary
FROM employees
WHERE salary * 1.1 > 60000;

-- OK: HAVING を使う(集計後の絞り込み)
SELECT department, AVG(salary) AS avg_sal
FROM employees
GROUP BY department
HAVING AVG(salary) > 60000;

-- OK: ORDER BY ではエイリアスが使える(SELECT より後に処理されるため)
SELECT name, salary * 1.1 AS new_salary
FROM employees
ORDER BY new_salary DESC;  -- エイリアスで並び替えられる

WHERE 句で行を絞り込む

WHERE 句で条件を指定すると、条件を満たす行だけが返ります。複数の条件は AND / OR で組み合わせます。

基本的な比較演算子
-- 等しい(=)
SELECT * FROM employees WHERE department = '開発';

-- 等しくない(<> または !=)
SELECT * FROM employees WHERE department <> '営業';

-- 大小比較
SELECT * FROM employees WHERE salary >= 60000;
SELECT * FROM employees WHERE salary <  50000;

-- 範囲(BETWEEN … AND …)
SELECT * FROM employees WHERE salary BETWEEN 50000 AND 70000;

-- リストの中に含まれる(IN)
SELECT * FROM employees WHERE department IN ('営業', '人事');

-- パターンマッチ(LIKE)
SELECT * FROM employees WHERE name LIKE '田%';  -- 「田」で始まる名前
演算子 意味 詳細記事
= / <> 等しい / 等しくない
> / < / >= / <= 大小比較
BETWEEN A AND B A 以上 B 以下の範囲 IN/BETWEEN完全ガイド
IN (値1, 値2, ...) リストのいずれかと一致 IN/BETWEEN完全ガイド
LIKE 'パターン' 文字列のパターン一致(% と _) LIKEで部分一致検索
IS NULL / IS NOT NULL NULL かどうか NULL以外を抽出する方法
NOT IN / NOT LIKE 否定条件 一致しないデータを抽出する方法
AND / OR で複数条件を組み合わせる
-- AND: 両方の条件を満たす行
SELECT * FROM employees
WHERE department = '開発'
  AND salary >= 70000;

-- OR: どちらかの条件を満たす行
SELECT * FROM employees
WHERE department = '営業'
   OR salary < 50000;

-- AND と OR の混在(OR のグループは括弧で囲む)
SELECT * FROM employees
WHERE (department = '営業' OR department = '人事')
  AND salary >= 50000;
AND と OR の優先順位に注意:
ANDOR より優先度が高いため、A OR B AND CA OR (B AND C) と解釈されます。意図した条件にならない場合は括弧 () で明示的にグループ化してください。

NULL の検索:IS NULL / IS NOT NULL

NULL は「値が存在しない(不明)」という特別な状態で、通常の比較演算子では比較できません。

NG と OK の違い
-- NG: = NULL は使えない(常に結果が返らない)
SELECT * FROM employees WHERE salary = NULL;    -- 0件(正しく動作しない)
SELECT * FROM employees WHERE manager_id = NULL; -- 0件

-- OK: IS NULL を使う
SELECT * FROM employees WHERE salary IS NULL;     -- 行6(渡辺)
SELECT * FROM employees WHERE manager_id IS NULL; -- 行1(田中)、行4(高橋)

-- OK: IS NOT NULL で NULL 以外を取得
SELECT * FROM employees WHERE salary IS NOT NULL;
NULL に = が使えない理由:
NULL は「不明な値」を表すため、NULL = NULL は TRUE ではなく UNKNOWN になります。SQL では WHERE 条件が UNKNOWN の行は結果に含まれません。NULL の比較には専用の IS NULL / IS NOT NULL を使ってください。NULL を含む列での集計や JOIN にも同様の注意が必要です。

DISTINCT で重複を除いてユニークな値を取得する

DISTINCT を付けると結果の重複行を除去します。

DISTINCT の使い方
-- 部門名の一覧(重複なし)
SELECT DISTINCT department FROM employees;

-- 結果:
-- department
-- ----------
-- 営業
-- 開発
-- 人事

-- 複数列に DISTINCT を付けた場合(すべての列の組み合わせで重複除去)
SELECT DISTINCT department, manager_id FROM employees;

-- DISTINCT と COUNT の組み合わせ(種類数のカウント)
SELECT COUNT(DISTINCT department) AS dept_count FROM employees;  -- 結果: 3

ORDER BY で結果を並び替える

ORDER BY で取得結果を昇順(ASC)または降順(DESC)に並び替えます。ORDER BY を指定しない場合、結果の順序は保証されません。

ORDER BY の基本
-- 給与の昇順(低い順)
SELECT name, salary FROM employees ORDER BY salary ASC;  -- ASC は省略可

-- 給与の降順(高い順)
SELECT name, salary FROM employees ORDER BY salary DESC;

-- 複数列でのソート(部門名の昇順、同じ部門内では給与の降順)
SELECT name, department, salary
FROM employees
ORDER BY department ASC, salary DESC;

-- NULL のソート順(NULL は ASC では最後、DESC では最初になるのがデフォルト)
-- MySQL: NULL は最小値として扱われる(ASC では先頭)
-- PostgreSQL / Oracle: ASC では最後、DESC では最初(NULLS LAST / NULLS FIRST で制御可)
SELECT name, salary FROM employees ORDER BY salary;
ORDER BY の詳しい使い方:
NULL の並び順制御(NULLS FIRST / NULLS LAST)、関数でのソート、エイリアスでのソート、RDBMS ごとの違いなどはORDER BY 完全ガイドで解説しています。

LIMIT で取得件数を制限する

大量データの中から先頭 N 件だけ取得したい場合や、ページネーションを実装する場合に LIMIT / OFFSET を使います。

LIMIT / OFFSET の基本
-- 給与上位5人を取得
SELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 5;

-- 11〜20件目を取得(ページネーション)
SELECT name, salary
FROM employees
ORDER BY id
LIMIT 10 OFFSET 10;  -- 10件取得、先頭10件をスキップ

-- RDBMS ごとの書き方の違い
-- MySQL / PostgreSQL / SQLite: LIMIT n OFFSET m
-- SQL Server:  SELECT TOP n ... / OFFSET m ROWS FETCH NEXT n ROWS ONLY
-- Oracle 12c+: FETCH FIRST n ROWS ONLY
LIMIT の詳しい使い方:
OFFSET を使ったページネーション実装・大きな OFFSET のパフォーマンス問題・RDBMS ごとの構文の違いはLIMIT/OFFSET完全ガイドで解説しています。

AS でエイリアス(別名)を付ける

列やテーブルに別名を付けると、SQL が読みやすくなり、計算結果に分かりやすい名前を付けられます。

列エイリアスとテーブルエイリアス
-- 列にエイリアスを付ける(日本語名も可、ただし引用符が必要な場合がある)
SELECT
    name                 AS 氏名,
    department           AS 部門,
    salary               AS 月給,
    salary * 12          AS 年収
FROM employees;

-- テーブルにエイリアスを付ける(特に JOIN で有用)
SELECT e.name, e.salary
FROM employees AS e
WHERE e.department = '開発';

-- AS は省略可能(可読性のため付けることを推奨)
SELECT name 氏名, salary 月給
FROM employees e;

JOIN で複数テーブルを結合して抽出する

実務では複数テーブルを結合して SELECT することが頻繁にあります。結合の基本だけ押さえておきましょう。

INNER JOIN と LEFT JOIN の基本
-- 従業員とそのマネージャー名を結合して取得
-- (自己結合:同じテーブルを2つの別名で使う)
SELECT
    e.name         AS employee_name,
    m.name         AS manager_name
FROM employees AS e
LEFT JOIN employees AS m ON e.manager_id = m.id;

-- 結果(manager_id が NULL の行も LEFT JOIN で取得できる):
-- employee_name | manager_name
-- -------------+-------------
-- 田中 太郎    | NULL         ← マネージャーなし(LEFT JOIN で残る)
-- 鈴木 花子    | 田中 太郎
-- ...
JOIN の詳しい使い方:
INNER JOIN・LEFT JOIN・RIGHT JOIN・FULL JOIN の違い、複数テーブルの結合、ON と USING の使い分けはJOIN完全ガイドで解説しています。

実務でよく使う SELECT パターン集

期間を絞り込んで最新N件を取得
-- 2022年以降に入社した従業員を新しい順に取得
SELECT name, hire_date, department
FROM employees
WHERE hire_date >= '2022-01-01'
ORDER BY hire_date DESC;

-- 最近1ヶ月以内のデータを取得(MySQL)
SELECT * FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH);

-- PostgreSQL
-- WHERE created_at >= NOW() - INTERVAL '1 month'
複数条件の組み合わせパターン
-- 開発部門で給与が60000以上、または営業部門で給与が50000以上
SELECT name, department, salary
FROM employees
WHERE (department = '開発'  AND salary >= 60000)
   OR (department = '営業' AND salary >= 50000)
ORDER BY department, salary DESC;

-- 特定の部門を除く全従業員(NOT IN)
SELECT name, department, salary
FROM employees
WHERE department NOT IN ('人事')
ORDER BY salary DESC;
NULL を含む集計の注意点
-- salary の NULL を除いた平均(AVG は NULL を自動除外)
SELECT AVG(salary)  AS avg_including_null_rows -- salaryがNULLの行は除外して平均
FROM employees;

-- NULL を 0 として扱いたい場合(COALESCE で NULL を置換)
SELECT AVG(COALESCE(salary, 0)) AS avg_treating_null_as_zero
FROM employees;

-- NULL が含まれる列の件数確認
SELECT
    COUNT(*)          AS total_rows,      -- 6
    COUNT(salary)     AS salary_filled,   -- 5(NULL除外)
    COUNT(*) - COUNT(salary) AS salary_nulls -- 1
FROM employees;

よくある質問(FAQ)

QWHERE と HAVING の違いを教えてください。
AWHERE は GROUP BY より前(SQL 実行順序②)に処理され、個々の行を絞り込みます。集計関数(COUNT、SUM、AVG など)は使えません。HAVING は GROUP BY より後(④)に処理され、グループ集計の結果を絞り込みます。HAVING COUNT(*) >= 2 のように集計関数が使えます。まとめると:「絞り込みたい条件が個々の行に対するものなら WHERE、集計結果に対するものなら HAVING」です。
QSELECT の実行結果の順番は保証されますか?
AORDER BY を指定しない場合、結果の順番は保証されません。データベースの内部処理(インデックスの使用状況・並列処理・テーブルの物理配置など)によって実行のたびに順番が変わる可能性があります。決まった順番で結果を受け取りたい場合は必ず ORDER BY を指定してください。
QSELECT * はいつ使っていいですか?
A開発中のデバッグ・テーブル内容の確認・一時的な調査など、インタラクティブな作業での利用は問題ありません。一方でアプリケーションに組み込む SQL・バッチ処理・ビュー定義などでは避けてください。テーブルに列が追加されたときにアプリ側で意図しない動作が起きるリスク、不要な列を転送するパフォーマンスのロスが生じます。
QNULL と空文字(”)は何が違いますか?
ANULL は「値が存在しない・不明」という状態です。空文字(”)は「長さ 0 の文字列」という値です。NULL は IS NULL でしか検索できませんが、空文字は = '' で検索できます。例: WHERE memo IS NULL(NULL 行のみ)と WHERE memo = ''(空文字のみ)は別の結果になります。NULL の詳細な扱いについてはNULL以外のデータを抽出する方法を参照してください。
Q大文字・小文字を区別しない検索をするには?
ARDBMS によって異なります。MySQL は照合順序(Collation)が _ci(case-insensitive)なら大文字小文字を区別しません。PostgreSQL は区別するため LOWER(column) = LOWER('検索値') または ILIKE 演算子(LIKE の大文字小文字無視版)を使います。Oracle は UPPER(column) = UPPER('検索値') か、照合順序の設定で制御します。

まとめ

やりたいこと 書き方・ポイント
全行・全列を取得 SELECT * FROM テーブル(本番では列名を明示)
特定列だけ取得 SELECT 列1, 列2 FROM テーブル
条件で絞り込む WHERE 条件(AND / OR / IN / BETWEEN / LIKE)
NULL を検索する WHERE 列 IS NULL= NULL は使えない)
重複を除いて取得 SELECT DISTINCT 列名
並び替え ORDER BY 列名 ASC|DESC
件数を制限 LIMIT n(RDBMS により構文が異なる)
別名を付ける 列名 AS エイリアス名
集計後に絞り込む GROUP BY ... HAVING 条件(WHERE ではなく HAVING を使う)
SQLの実行順序 FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT

各条件演算子の詳細についてはIN/BETWEEN/EXISTS完全ガイドLIKEで部分一致検索する方法LIMIT/OFFSET完全ガイドもあわせてご覧ください。データの重複を除く方法はDISTINCTで重複データを削除する方法で詳しく解説しています。