0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SQL実践ドリル 第1回】SELECT の基本 ― データの取得・絞り込み・並べ替えをマスターする

0
Last updated at Posted at 2026-02-25

はじめに

【SQL実践ドリル】 シリーズでは、実際にSQLを書いて動かしながらスキルを身につけることを目指します。第1回のテーマは SELECT 文の基本 です。

データの取得・絞り込み・並べ替えは、SQLのあらゆる操作の土台になります。ここをしっかり押さえれば、後の集計やJOINもスムーズに理解できます。

対象読者

  • SQLの基本構文を学び始めた方
  • SELECT 文を実際に書いて練習したい方
  • MySQL を使っている方(PostgreSQL との差異は都度補足します)

難易度の目安

マーク レベル 説明
基本 基本構文そのまま
⭐⭐ 応用 複数の構文を組み合わせる
⭐⭐⭐ チャレンジ 実務を意識した問題

サンプルデータベース

以下のCREATE TABLE文とINSERT文を実行して、練習用のデータベースを準備してください。全3回を通して同じデータを使います。

-- 部署テーブル
CREATE TABLE departments (
    department_id INT PRIMARY KEY,
    department_name VARCHAR(50) NOT NULL,
    location VARCHAR(50)
);

INSERT INTO departments VALUES
(1, '営業部', '東京'),
(2, '開発部', '大阪'),
(3, '人事部', '東京'),
(4, '経理部', '名古屋'),
(5, 'マーケティング部', '福岡');

-- 社員テーブル
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    department_id INT,
    hire_date DATE NOT NULL,
    salary INT NOT NULL,
    manager_id INT,
    FOREIGN KEY (department_id) REFERENCES departments(department_id),
    FOREIGN KEY (manager_id) REFERENCES employees(employee_id)
);

INSERT INTO employees VALUES
(1, '田中太郎', 1, '2020-04-01', 350000, NULL),
(2, '佐藤花子', 2, '2019-07-15', 420000, 1),
(3, '鈴木一郎', 1, '2021-01-10', 300000, 1),
(4, '高橋美咲', 3, '2018-09-01', 380000, NULL),
(5, '伊藤健太', 2, '2022-03-20', 450000, 2),
(6, '渡辺さくら', 2, '2023-06-01', 280000, 2),
(7, '山本大輔', 4, '2020-11-15', 330000, 4),
(8, '中村真理', 1, '2021-08-01', 310000, 1),
(9, '小林誠', 3, '2024-01-15', 270000, 4),
(10, '加藤優子', NULL, '2023-10-01', 290000, NULL);

-- 顧客テーブル
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    customer_name VARCHAR(100) NOT NULL,
    email VARCHAR(100),
    prefecture VARCHAR(20),
    created_at DATE NOT NULL
);

INSERT INTO customers VALUES
(1, '株式会社ABC', 'abc@example.com', '東京都', '2023-01-15'),
(2, 'DEFコーポレーション', 'def@example.com', '大阪府', '2023-03-20'),
(3, 'GHI商事', 'ghi@example.com', '東京都', '2023-06-10'),
(4, 'JKLテクノロジー', NULL, '愛知県', '2024-01-05'),
(5, 'MNOサービス', 'mno@example.com', '福岡県', '2024-04-20');

-- 商品テーブル
CREATE TABLE products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    category VARCHAR(50) NOT NULL,
    price INT NOT NULL,
    stock INT NOT NULL DEFAULT 0
);

INSERT INTO products VALUES
(1, 'ノートPC Pro', 'パソコン', 198000, 50),
(2, 'ワイヤレスマウス', '周辺機器', 3500, 200),
(3, 'USBハブ 7ポート', '周辺機器', 4800, 150),
(4, '4Kモニター 27インチ', 'モニター', 45000, 30),
(5, 'メカニカルキーボード', '周辺機器', 12000, 80),
(6, 'ノートPC Light', 'パソコン', 89000, 100),
(7, 'Webカメラ HD', '周辺機器', 6500, 120),
(8, 'ゲーミングモニター', 'モニター', 65000, 20);

-- 注文テーブル
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT NOT NULL,
    employee_id INT NOT NULL,
    order_date DATE NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
    FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
);

INSERT INTO orders VALUES
(1, 1, 1, '2024-07-01'),
(2, 2, 3, '2024-07-05'),
(3, 1, 1, '2024-07-10'),
(4, 3, 8, '2024-08-01'),
(5, 4, 3, '2024-08-15'),
(6, 2, 1, '2024-09-01'),
(7, 5, 8, '2024-09-10'),
(8, 1, 3, '2024-10-01'),
(9, 3, 1, '2024-10-15'),
(10, 4, 8, '2024-11-01');

-- 注文明細テーブル
CREATE TABLE order_details (
    detail_id INT PRIMARY KEY,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

INSERT INTO order_details VALUES
(1, 1, 1, 2),
(2, 1, 2, 5),
(3, 2, 4, 1),
(4, 2, 5, 3),
(5, 3, 6, 1),
(6, 3, 3, 2),
(7, 4, 1, 1),
(8, 4, 7, 4),
(9, 5, 2, 10),
(10, 5, 5, 2),
(11, 6, 8, 1),
(12, 6, 2, 3),
(13, 7, 6, 2),
(14, 7, 3, 5),
(15, 8, 1, 1),
(16, 8, 4, 2),
(17, 9, 5, 1),
(18, 9, 7, 3),
(19, 10, 6, 3),
(20, 10, 2, 8);

SELECT 文の基本構文

SELECT 列名1, 列名2, ...
FROM テーブル名
WHERE 条件
ORDER BY 列名 [ASC|DESC]
LIMIT 件数;

SQL文は上記の順序で記述します。各句(クローズ)の役割は以下のとおりです。

役割
SELECT 取得する列を指定する。* で全列
FROM 対象のテーブルを指定する
WHERE 行の絞り込み条件を指定する
ORDER BY 結果の並べ替えを指定する(ASC = 昇順、DESC = 降順)
LIMIT 取得件数を制限する

WHERE句で使える主な演算子・キーワード

比較演算子

WHERE salary >= 350000    -- 350000以上
WHERE salary <> 350000    -- 350000以外(!= も同義)
演算子 意味
= 等しい
<> または != 等しくない
> より大きい
>= 以上
< より小さい
<= 以下

LIKE(パターンマッチ)

文字列の部分一致検索に使います。

WHERE product_name LIKE '%モニター%'   -- 「モニター」を含む
WHERE name LIKE '田中%'                -- 「田中」で始まる
WHERE email LIKE '%@example.com'       -- 「@example.com」で終わる
  • % は任意の0文字以上にマッチ
  • _ は任意の1文字にマッチ

IN(リスト内の値と一致)

WHERE department_id IN (1, 2, 3)
-- 以下と同義:
-- WHERE department_id = 1 OR department_id = 2 OR department_id = 3

IN はサブクエリと組み合わせることもできます。

WHERE department_id IN (SELECT department_id FROM departments WHERE location = '東京')

BETWEEN(範囲指定)

WHERE salary BETWEEN 300000 AND 400000
-- 以下と同義:
-- WHERE salary >= 300000 AND salary <= 400000

BETWEEN両端の値を含みます(閉区間)。

IS NULL / IS NOT NULL

SQLでは NULL は「値が存在しない」ことを表します。NULL の比較には = ではなく IS NULL / IS NOT NULL を使います。

WHERE manager_id IS NULL       -- マネージャーがいない
WHERE email IS NOT NULL        -- メールアドレスが登録されている

注意: WHERE manager_id = NULL は正しく動作しません。NULL との比較は常に UNKNOWN になるためです。

ORDER BY(並べ替え)

ORDER BY salary DESC              -- 給与の降順
ORDER BY category ASC, price DESC -- カテゴリ昇順 → 価格降順
  • ASC(昇順)はデフォルトなので省略可能
  • 複数列を指定すると、先に書いた列が優先されます

LIMIT(件数制限)

LIMIT 5        -- 先頭5件のみ
LIMIT 5, 3     -- 6件目から3件(MySQL独自構文)
LIMIT 3 OFFSET 5  -- 同上(MySQL・PostgreSQL共通)

PostgreSQL との違い: LIMIT 5, 3 構文はMySQLのみ対応です。PostgreSQLでは LIMIT 3 OFFSET 5 を使ってください。


問題

問題 1 ⭐(基本):全社員の名前と給与を表示

社員テーブル (employees) から、全社員の name(名前)と salary(給与)を取得してください。

期待結果:

+----------------+--------+
| name           | salary |
+----------------+--------+
| 田中太郎       | 350000 |
| 佐藤花子       | 420000 |
| 鈴木一郎       | 300000 |
| 高橋美咲       | 380000 |
| 伊藤健太       | 450000 |
| 渡辺さくら     | 280000 |
| 山本大輔       | 330000 |
| 中村真理       | 310000 |
| 小林誠         | 270000 |
| 加藤優子       | 290000 |
+----------------+--------+
10 rows in set
模範解答
SELECT name, salary
FROM employees;

解説: SELECT に取得したい列名をカンマ区切りで指定します。SELECT * とすると全列を取得できますが、必要な列だけを指定する習慣をつけましょう。実務ではネットワーク転送量やパフォーマンスに差が出ます。


問題 2 ⭐(基本):給与が350,000円以上の社員

社員テーブルから、給与 (salary) が350,000円以上の社員の name(名前)、salary(給与)を取得してください。

期待結果:

+------------+--------+
| name       | salary |
+------------+--------+
| 田中太郎   | 350000 |
| 佐藤花子   | 420000 |
| 高橋美咲   | 380000 |
| 伊藤健太   | 450000 |
+------------+--------+
4 rows in set
模範解答
SELECT name, salary
FROM employees
WHERE salary >= 350000;

解説: WHERE 句で比較演算子 >=(以上)を使って条件を指定します。350,000円ちょうどの田中太郎さんも含まれる点に注意してください。「より大きい」にしたい場合は > を使います。


問題 3 ⭐(基本):営業部の社員を入社日の新しい順で

営業部(department_id = 1)の社員の name(名前)、hire_date(入社日)、salary(給与)を、入社日が新しい順に並べて取得してください。

期待結果:

+------------+------------+--------+
| name       | hire_date  | salary |
+------------+------------+--------+
| 中村真理   | 2021-08-01 | 310000 |
| 鈴木一郎   | 2021-01-10 | 300000 |
| 田中太郎   | 2020-04-01 | 350000 |
+------------+------------+--------+
3 rows in set
模範解答
SELECT name, hire_date, salary
FROM employees
WHERE department_id = 1
ORDER BY hire_date DESC;

解説: ORDER BY hire_date DESC で入社日の降順(新しい日付が先)に並べます。DESC を省略すると昇順(ASC)になり、古い日付が先に表示されます。


問題 4 ⭐(基本):商品名に「モニター」を含む商品

商品テーブル (products) から、商品名 (product_name) に「モニター」を含む商品の product_name(商品名)、price(価格)を取得してください。

期待結果:

+--------------------------+-------+
| product_name             | price |
+--------------------------+-------+
| 4Kモニター 27インチ      | 45000 |
| ゲーミングモニター       | 65000 |
+--------------------------+-------+
2 rows in set
模範解答
SELECT product_name, price
FROM products
WHERE product_name LIKE '%モニター%';

解説: LIKE '%モニター%' で「モニター」を含む文字列にマッチします。% は0文字以上の任意の文字列を表すワイルドカードです。先頭の % を外して LIKE 'モニター%' とすれば「モニターで始まる」になります。

注意: MySQLではデフォルトの照合順序(utf8mb4_general_ci など)で LIKE は大文字・小文字を区別しません。区別したい場合は LIKE BINARY または COLLATE を指定します。


問題 5 ⭐⭐(応用):給与が300,000〜400,000円の社員(BETWEEN)

社員テーブルから、給与が300,000円以上400,000円以下の社員の name(名前)、salary(給与)を給与の昇順で取得してください。

期待結果:

+----------------+--------+
| name           | salary |
+----------------+--------+
| 鈴木一郎       | 300000 |
| 中村真理       | 310000 |
| 山本大輔       | 330000 |
| 田中太郎       | 350000 |
| 高橋美咲       | 380000 |
+----------------+--------+
5 rows in set
模範解答
SELECT name, salary
FROM employees
WHERE salary BETWEEN 300000 AND 400000
ORDER BY salary ASC;

解説: BETWEEN 300000 AND 400000 は「300,000以上かつ400,000以下」を意味します。両端の値を含む(閉区間)ことに注意してください。同じ条件は WHERE salary >= 300000 AND salary <= 400000 とも書けます。


問題 6 ⭐⭐(応用):東京または大阪にある部署に所属する社員(IN + サブクエリ)

東京 ('東京') または大阪 ('大阪') にある部署に所属する社員の name(名前)、department_id を取得してください。JOINは使わず、WHERE ... IN (サブクエリ) で解いてください。

確認: departments テーブルの location が '東京' の department_id は 1, 3。'大阪' は 2。

期待結果:

+----------------+---------------+
| name           | department_id |
+----------------+---------------+
| 田中太郎       |             1 |
| 佐藤花子       |             2 |
| 鈴木一郎       |             1 |
| 高橋美咲       |             3 |
| 伊藤健太       |             2 |
| 渡辺さくら     |             2 |
| 中村真理       |             1 |
| 小林誠         |             3 |
+----------------+---------------+
8 rows in set
模範解答
SELECT name, department_id
FROM employees
WHERE department_id IN (
    SELECT department_id
    FROM departments
    WHERE location IN ('東京', '大阪')
);

解説: サブクエリ(内側の SELECT)で東京・大阪にある部署の department_id を取得し、外側の WHERE ... IN でその結果に含まれる社員を絞り込みます。

サブクエリの結果は (1, 2, 3) になります(東京: 営業部=1, 人事部=3、大阪: 開発部=2)。

加藤優子さん(department_id = NULL)は IN の条件に合致しないため結果に含まれません。NULLINTRUE にならないからです。


問題 7 ⭐⭐(応用):商品をカテゴリ別に価格の高い順で表示(ORDER BY 複数列)

商品テーブルから全商品の category(カテゴリ)、product_name(商品名)、price(価格)を取得してください。まずカテゴリの昇順で並べ、同じカテゴリ内では価格の降順で並べてください。

期待結果:

+------------+--------------------------+--------+
| category   | product_name             | price  |
+------------+--------------------------+--------+
| パソコン   | ノートPC Pro             | 198000 |
| パソコン   | ノートPC Light           |  89000 |
| モニター   | ゲーミングモニター       |  65000 |
| モニター   | 4Kモニター 27インチ      |  45000 |
| 周辺機器   | メカニカルキーボード     |  12000 |
| 周辺機器   | Webカメラ HD             |   6500 |
| 周辺機器   | USBハブ 7ポート          |   4800 |
| 周辺機器   | ワイヤレスマウス         |   3500 |
+------------+--------------------------+--------+
8 rows in set
模範解答
SELECT category, product_name, price
FROM products
ORDER BY category ASC, price DESC;

解説: ORDER BY にカンマ区切りで複数列を指定すると、先に書いた列が優先されます。まず category ASC(カテゴリ昇順)で並べ、カテゴリが同じ行どうしは price DESC(価格降順)で並びます。

補足: MySQLのデフォルト照合順序では、日本語文字列のソート順は文字コード順になります。ここでは「パソコン」「モニター」「周辺機器」の順で表示されます(UTF-8のコードポイント順)。


問題 8 ⭐⭐(応用):上位3件の高額商品(LIMIT)

商品テーブルから、価格が高い順に上位3件の商品の product_name(商品名)と price(価格)を取得してください。

期待結果:

+--------------------------+--------+
| product_name             | price  |
+--------------------------+--------+
| ノートPC Pro             | 198000 |
| ノートPC Light           |  89000 |
| ゲーミングモニター       |  65000 |
+--------------------------+--------+
3 rows in set
模範解答
SELECT product_name, price
FROM products
ORDER BY price DESC
LIMIT 3;

解説: ORDER BY price DESC で価格の高い順に並べ、LIMIT 3 で先頭3件だけを取得します。LIMITORDER BY と組み合わせて「トップN件」を取得するのによく使われます。

PostgreSQL との違い: PostgreSQL では LIMIT 3 に加えて、SQL標準に準拠した FETCH FIRST 3 ROWS ONLY も使えます。MySQL 8.0 でもこの構文はサポートされています。


問題 9 ⭐⭐⭐(チャレンジ):マネージャーがいない社員(IS NULL)

マネージャー (manager_id) が設定されていない社員の employee_id(社員ID)、name(名前)、manager_id を取得してください。

期待結果:

+-------------+------------+------------+
| employee_id | name       | manager_id |
+-------------+------------+------------+
|           1 | 田中太郎   |       NULL |
|           4 | 高橋美咲   |       NULL |
|          10 | 加藤優子   |       NULL |
+-------------+------------+------------+
3 rows in set
模範解答
SELECT employee_id, name, manager_id
FROM employees
WHERE manager_id IS NULL;

解説: NULL は「値が存在しない」ことを示す特殊な値です。= NULL では判定できず、必ず IS NULL を使います。

-- NG: これは正しく動作しない
SELECT * FROM employees WHERE manager_id = NULL;

-- OK: IS NULL を使う
SELECT * FROM employees WHERE manager_id IS NULL;

manager_id = NULLNULL = NULL の比較になりますが、SQLの3値論理では NULL = NULLTRUE でも FALSE でもなく UNKNOWN となり、WHERE 句は UNKNOWN の行を除外します。


問題 10 ⭐⭐⭐(チャレンジ):2023年以降に入社した社員を給与の高い順で上位5名

2023年1月1日以降(hire_date >= '2023-01-01')に入社した社員の name(名前)、hire_date(入社日)、salary(給与)を、給与が高い順に並べて上位5名を取得してください。

確認: 2023年以降に入社した社員:

  • (10, '加藤優子', '2023-10-01', 290000)
  • (6, '渡辺さくら', '2023-06-01', 280000)
  • (9, '小林誠', '2024-01-15', 270000)

3名しかいないため、結果は3行になります。

期待結果:

+----------------+------------+--------+
| name           | hire_date  | salary |
+----------------+------------+--------+
| 加藤優子       | 2023-10-01 | 290000 |
| 渡辺さくら     | 2023-06-01 | 280000 |
| 小林誠         | 2024-01-15 | 270000 |
+----------------+------------+--------+
3 rows in set
模範解答
SELECT name, hire_date, salary
FROM employees
WHERE hire_date >= '2023-01-01'
ORDER BY salary DESC
LIMIT 5;

解説: WHERE で入社日を絞り込み、ORDER BY で並べ替え、LIMIT で件数を制限しています。複数の句を組み合わせるのが実践的な使い方です。

条件に合致する社員は3名のみなので、LIMIT 5 を指定しても結果は3行になります。LIMIT は「最大N件」であり、データがN件に満たない場合はある分だけ返されます。

日付の比較には >= の他に BETWEEN も使えます。

-- 2023年中に入社した社員を取得する場合
WHERE hire_date BETWEEN '2023-01-01' AND '2023-12-31'

補足: MySQLでは文字列 '2023-01-01' をDATE型と比較すると暗黙的に型変換されます。明示的に変換する場合は CAST('2023-01-01' AS DATE)DATE('2023-01-01') を使います。


まとめ

第1回では、SELECT文の基本的な使い方を学びました。

構文 用途
SELECT 列名 FROM テーブル名 指定した列のデータを取得
WHERE 条件 行の絞り込み
LIKE '%文字列%' パターンマッチ(部分一致など)
IN (値1, 値2, ...) リスト内の値と一致
BETWEEN a AND b 範囲指定(両端含む)
IS NULL / IS NOT NULL NULLの判定
ORDER BY 列名 [ASC|DESC] 並べ替え
LIMIT n 取得件数の制限

次回(第2回)は 集計関数と GROUP BY を扱います。COUNT, SUM, AVG などを使って、データの分析に挑戦しましょう。

参考


@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?