はじめに

データベースを操作する上で最も基本的かつ頻繁に使用するのがSELECT文です。アプリケーション開発においてデータの取得は避けて通れない作業であり、SELECT文を適切に書けるかどうかが開発効率に直結します。

本記事では、PostgreSQLにおけるSELECT文の基本構文から、WHERE句による条件指定、ORDER BY/LIMIT/OFFSETによる並び替えとページング、集計関数(COUNT/SUM/AVG/MAX/MIN)とGROUP BY、DISTINCT、エイリアスの活用まで、実践的なサンプルコードとともに解説します。

この記事を読むことで、以下のことができるようになります。

  • SELECT文の基本構文を理解し、テーブルからデータを取得できる
  • WHERE句を使って必要なデータだけを抽出できる
  • ORDER BY/LIMIT/OFFSETで結果の並び替えとページングを実装できる
  • 集計関数とGROUP BYを使ってデータの集計・分析ができる
  • DISTINCTとエイリアスを活用してクエリを効率的に記述できる

前提条件

  • PostgreSQL 14以降がインストールされていること
  • psqlまたはGUIツール(pgAdmin、DBeaver等)でデータベースに接続できること
  • 基本的なデータベースの概念(テーブル、行、列)を理解していること

サンプルデータの準備

本記事で使用するサンプルテーブルとデータを準備します。以下のSQLを実行してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
-- サンプルデータベースの作成(必要に応じて)
-- CREATE DATABASE sample_db;

-- 商品テーブルの作成
CREATE TABLE products (
    product_id   SERIAL PRIMARY KEY,
    name         VARCHAR(100) NOT NULL,
    category     VARCHAR(50) NOT NULL,
    price        INTEGER NOT NULL,
    stock        INTEGER DEFAULT 0,
    created_at   TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 注文テーブルの作成
CREATE TABLE orders (
    order_id     SERIAL PRIMARY KEY,
    product_id   INTEGER REFERENCES products(product_id),
    quantity     INTEGER NOT NULL,
    order_date   DATE NOT NULL,
    customer_id  INTEGER NOT NULL
);

-- 商品データの挿入
INSERT INTO products (name, category, price, stock) VALUES
('ノートPC', '電子機器', 120000, 25),
('マウス', '電子機器', 3500, 150),
('キーボード', '電子機器', 8000, 80),
('デスク', '家具', 45000, 12),
('オフィスチェア', '家具', 35000, 20),
('モニター', '電子機器', 55000, 30),
('ヘッドセット', '電子機器', 12000, 45),
('本棚', '家具', 28000, 8),
('デスクライト', '照明', 6500, 60),
('ペン(10本セット)', '文具', 500, 200);

-- 注文データの挿入
INSERT INTO orders (product_id, quantity, order_date, customer_id) VALUES
(1, 2, '2025-12-01', 101),
(2, 5, '2025-12-01', 102),
(3, 3, '2025-12-02', 101),
(1, 1, '2025-12-03', 103),
(4, 1, '2025-12-03', 104),
(5, 2, '2025-12-04', 105),
(6, 3, '2025-12-05', 101),
(2, 10, '2025-12-05', 106),
(7, 4, '2025-12-06', 107),
(8, 1, '2025-12-07', 108),
(9, 5, '2025-12-08', 109),
(10, 20, '2025-12-08', 110),
(1, 3, '2025-12-10', 111),
(3, 2, '2025-12-10', 112),
(6, 1, '2025-12-11', 113);

SELECT文の基本構文

全カラムを取得する

最もシンプルなSELECT文は、テーブルから全てのカラムを取得する形式です。アスタリスク(*)を使用することで、テーブル内の全カラムを一度に取得できます。

1
SELECT * FROM products;

実行結果:

product_id name category price stock created_at
1 ノートPC 電子機器 120000 25 2026-01-03 …
2 マウス 電子機器 3500 150 2026-01-03 …

ただし、本番環境ではSELECT *の使用は推奨されません。必要なカラムを明示的に指定することで、以下のメリットがあります。

  • ネットワーク転送量の削減
  • クエリの意図が明確になる
  • テーブル構造の変更に対する耐性向上

特定のカラムを取得する

必要なカラムのみを取得する場合は、カラム名をカンマ区切りで列挙します。

1
SELECT name, category, price FROM products;

実行結果:

name category price
ノートPC 電子機器 120000
マウス 電子機器 3500
キーボード 電子機器 8000

SELECT文の処理順序

SELECT文は記述順と実行順が異なります。理解しておくとクエリのデバッグに役立ちます。

flowchart TD
    A["1. FROM句 - テーブルの特定"] --> B["2. WHERE句 - 行のフィルタリング"]
    B --> C["3. GROUP BY句 - グループ化"]
    C --> D["4. HAVING句 - グループのフィルタリング"]
    D --> E["5. SELECT句 - カラムの選択・計算"]
    E --> F["6. DISTINCT - 重複排除"]
    F --> G["7. ORDER BY句 - 並び替え"]
    G --> H["8. LIMIT/OFFSET - 結果の制限"]

WHERE句による条件指定

基本的な比較演算子

WHERE句を使用して、条件に合致する行のみを取得できます。

演算子 説明 使用例
= 等しい price = 3500
<> または != 等しくない category <> '家具'
< より小さい price < 10000
> より大きい stock > 50
<= 以下 price <= 50000
>= 以上 stock >= 20
1
2
-- 価格が10000円以下の商品を取得
SELECT name, price FROM products WHERE price <= 10000;

実行結果:

name price
マウス 3500
キーボード 8000
デスクライト 6500
ペン(10本セット) 500

論理演算子(AND, OR, NOT)

複数の条件を組み合わせる場合は論理演算子を使用します。

1
2
3
4
-- 電子機器カテゴリで、かつ価格が50000円以上の商品
SELECT name, category, price 
FROM products 
WHERE category = '電子機器' AND price >= 50000;

実行結果:

name category price
ノートPC 電子機器 120000
モニター 電子機器 55000
1
2
3
4
-- 電子機器または家具カテゴリの商品
SELECT name, category, price 
FROM products 
WHERE category = '電子機器' OR category = '家具';

BETWEEN句

範囲を指定する場合はBETWEEN句が便利です。

1
2
3
4
-- 価格が5000円以上30000円以下の商品
SELECT name, price 
FROM products 
WHERE price BETWEEN 5000 AND 30000;

実行結果:

name price
キーボード 8000
ヘッドセット 12000
本棚 28000
デスクライト 6500

BETWEENは境界値を含む(両端を含む)ことに注意してください。

IN句

複数の値のいずれかに一致するかを判定する場合はIN句を使用します。

1
2
3
4
-- 電子機器、家具、文具のいずれかに該当する商品
SELECT name, category, price 
FROM products 
WHERE category IN ('電子機器', '家具', '文具');

LIKE句によるパターンマッチング

文字列の部分一致検索にはLIKE句を使用します。

パターン 説明
% 0文字以上の任意の文字列
_ 任意の1文字
1
2
3
4
5
-- 名前に「PC」を含む商品
SELECT name, price FROM products WHERE name LIKE '%PC%';

-- 名前が「デスク」で始まる商品
SELECT name, price FROM products WHERE name LIKE 'デスク%';

実行結果(1つ目のクエリ):

name price
ノートPC 120000

NULL値の判定

NULL値の判定にはIS NULLまたはIS NOT NULLを使用します。= NULLでは正しく判定できません。

1
2
-- 在庫がNULLでない商品(通常は全てがこれに該当)
SELECT name, stock FROM products WHERE stock IS NOT NULL;

ORDER BYによる並び替え

昇順と降順

ORDER BY句を使用して結果を並び替えられます。

1
2
3
4
5
-- 価格の昇順(安い順)で取得
SELECT name, price FROM products ORDER BY price ASC;

-- 価格の降順(高い順)で取得
SELECT name, price FROM products ORDER BY price DESC;

ASCは省略可能で、デフォルトは昇順です。

実行結果(降順):

name price
ノートPC 120000
モニター 55000
デスク 45000
オフィスチェア 35000

複数カラムでの並び替え

複数のカラムで並び替える場合は、カンマ区切りで指定します。最初のカラムで同じ値の場合、次のカラムで並び替えられます。

1
2
3
4
-- カテゴリ昇順、その中で価格降順で取得
SELECT name, category, price 
FROM products 
ORDER BY category ASC, price DESC;

実行結果:

name category price
オフィスチェア 家具 35000
デスク 家具 45000
ノートPC 電子機器 120000
モニター 電子機器 55000

NULLの扱い

ORDER BYでのNULL値の扱いは、NULLS FIRSTまたはNULLS LASTで制御できます。

1
2
3
4
5
-- NULL値を最後に配置
SELECT name, stock FROM products ORDER BY stock DESC NULLS LAST;

-- NULL値を最初に配置
SELECT name, stock FROM products ORDER BY stock ASC NULLS FIRST;

LIMITとOFFSETによるページング

取得件数を制限する

LIMIT句で取得する行数を制限できます。

1
2
-- 上位5件のみ取得
SELECT name, price FROM products ORDER BY price DESC LIMIT 5;

実行結果:

name price
ノートPC 120000
モニター 55000
デスク 45000
オフィスチェア 35000
本棚 28000

OFFSETで取得開始位置を指定

OFFSET句と組み合わせることで、ページング処理を実装できます。

1
2
3
4
5
-- 6件目から5件取得(2ページ目相当)
SELECT name, price 
FROM products 
ORDER BY price DESC 
LIMIT 5 OFFSET 5;

実行結果:

name price
ヘッドセット 12000
キーボード 8000
デスクライト 6500
マウス 3500
ペン(10本セット) 500

ページング実装のパターン

Webアプリケーションでのページング実装では、以下の計算式を使用します。

1
2
3
4
5
6
7
8
-- ページ番号とページサイズからOFFSETを計算
-- OFFSET = (ページ番号 - 1) × ページサイズ

-- 例: 1ページ10件で3ページ目を表示
SELECT name, price 
FROM products 
ORDER BY product_id 
LIMIT 10 OFFSET 20;

なお、大量のデータに対してOFFSET値が大きくなるとパフォーマンスが低下します。その場合はカーソルベースのページングを検討してください。

集計関数の活用

基本的な集計関数

PostgreSQLで頻繁に使用する集計関数を紹介します。

関数 説明 NULL値の扱い
COUNT(*) 行数をカウント NULLを含む全行をカウント
COUNT(column) 指定カラムの非NULL値をカウント NULLは除外
SUM(column) 合計値を計算 NULLは除外
AVG(column) 平均値を計算 NULLは除外
MAX(column) 最大値を取得 NULLは除外
MIN(column) 最小値を取得 NULLは除外
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
-- 商品の総数
SELECT COUNT(*) AS total_products FROM products;

-- 電子機器カテゴリの商品数
SELECT COUNT(*) AS electronics_count 
FROM products 
WHERE category = '電子機器';

-- 価格の合計、平均、最大、最小
SELECT 
    SUM(price) AS total_price,
    AVG(price) AS avg_price,
    MAX(price) AS max_price,
    MIN(price) AS min_price
FROM products;

実行結果(3つ目のクエリ):

total_price avg_price max_price min_price
313500 31350.00 120000 500

COUNTの使い分け

COUNT(*)COUNT(column)の違いを理解しておくことが重要です。

1
2
3
4
5
6
7
8
-- 全行数をカウント(NULLがあってもカウント)
SELECT COUNT(*) FROM products;

-- stock列の非NULL値のみカウント
SELECT COUNT(stock) FROM products;

-- stockの値が異なるもののみカウント(重複排除)
SELECT COUNT(DISTINCT category) FROM products;

GROUP BYによるグループ化

基本的なグループ化

GROUP BY句を使用して、特定のカラムの値でグループ化し、グループごとの集計ができます。

1
2
3
4
-- カテゴリ別の商品数
SELECT category, COUNT(*) AS product_count 
FROM products 
GROUP BY category;

実行結果:

category product_count
電子機器 5
家具 3
照明 1
文具 1
1
2
3
4
5
6
7
8
9
-- カテゴリ別の平均価格と在庫合計
SELECT 
    category,
    COUNT(*) AS product_count,
    AVG(price) AS avg_price,
    SUM(stock) AS total_stock
FROM products
GROUP BY category
ORDER BY avg_price DESC;

実行結果:

category product_count avg_price total_stock
電子機器 5 39700.00 330
家具 3 36000.00 40
照明 1 6500.00 60
文具 1 500.00 200

複数カラムでのグループ化

複数のカラムでグループ化することも可能です。

1
2
3
4
5
6
7
8
-- 注文日と商品IDごとの注文数量合計
SELECT 
    order_date,
    product_id,
    SUM(quantity) AS total_quantity
FROM orders
GROUP BY order_date, product_id
ORDER BY order_date, product_id;

HAVING句によるグループのフィルタリング

GROUP BY後のグループに対して条件を指定する場合はHAVING句を使用します。WHERE句は行のフィルタリング、HAVING句はグループのフィルタリングという違いがあります。

1
2
3
4
5
-- 商品数が2以上のカテゴリのみ取得
SELECT category, COUNT(*) AS product_count
FROM products
GROUP BY category
HAVING COUNT(*) >= 2;

実行結果:

category product_count
電子機器 5
家具 3
1
2
3
4
5
6
7
8
9
-- 平均価格が30000円以上のカテゴリ
SELECT 
    category,
    AVG(price) AS avg_price,
    COUNT(*) AS product_count
FROM products
GROUP BY category
HAVING AVG(price) >= 30000
ORDER BY avg_price DESC;

WHEREとHAVINGの違い

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- WHEREは行のフィルタリング(GROUP BY前に適用)
-- HAVINGはグループのフィルタリング(GROUP BY後に適用)

SELECT 
    category,
    AVG(price) AS avg_price
FROM products
WHERE price >= 5000          -- 5000円以上の商品のみを対象
GROUP BY category
HAVING AVG(price) >= 20000;  -- 平均が20000円以上のカテゴリのみ表示

DISTINCTによる重複排除

単一カラムの重複排除

DISTINCT句を使用して、重複する値を排除できます。

1
2
-- 存在するカテゴリの一覧を取得
SELECT DISTINCT category FROM products;

実行結果:

category
電子機器
家具
照明
文具

複数カラムの重複排除

複数カラムを指定した場合、全てのカラムの値の組み合わせが一致する行が重複とみなされます。

1
2
3
4
-- 注文のあった日付と商品IDの組み合わせを取得
SELECT DISTINCT order_date, product_id 
FROM orders
ORDER BY order_date;

DISTINCT ONによる高度な重複排除

PostgreSQL独自の機能として、DISTINCT ONを使用すると、指定したカラムの値ごとに最初の1行のみを取得できます。

1
2
3
4
-- 各カテゴリで最も安い商品を1つずつ取得
SELECT DISTINCT ON (category) category, name, price
FROM products
ORDER BY category, price ASC;

実行結果:

category name price
家具 本棚 28000
文具 ペン(10本セット) 500
照明 デスクライト 6500
電子機器 マウス 3500

DISTINCT ONを使用する場合、ORDER BY句の先頭はDISTINCT ONで指定したカラムと一致させる必要があります。

エイリアス(AS)の活用

カラムエイリアス

ASキーワードを使用して、カラムに別名を付けることができます。

1
2
3
4
5
6
SELECT 
    name AS 商品名,
    price AS 価格,
    stock AS 在庫数,
    price * stock AS 在庫金額
FROM products;

実行結果:

商品名 価格 在庫数 在庫金額
ノートPC 120000 25 3000000
マウス 3500 150 525000

ASキーワードは省略可能ですが、可読性のために明示的に記述することを推奨します。

1
2
3
4
5
-- ASを省略した書き方(非推奨)
SELECT name 商品名, price 価格 FROM products;

-- ASを明示した書き方(推奨)
SELECT name AS 商品名, price AS 価格 FROM products;

テーブルエイリアス

FROM句でテーブルに別名を付けることで、クエリを簡潔に記述できます。特に複数テーブルを結合する際に有効です。

1
2
3
4
5
6
7
8
SELECT 
    p.name AS 商品名,
    p.price AS 価格,
    o.quantity AS 注文数量,
    o.order_date AS 注文日
FROM products AS p, orders AS o
WHERE p.product_id = o.product_id
ORDER BY o.order_date DESC;

計算式とエイリアス

集計関数や計算式の結果にエイリアスを付けると、結果の可読性が向上します。

1
2
3
4
5
6
7
8
9
SELECT 
    category AS カテゴリ,
    COUNT(*) AS 商品数,
    ROUND(AVG(price), 0) AS 平均価格,
    SUM(stock) AS 総在庫数,
    SUM(price * stock) AS 総在庫金額
FROM products
GROUP BY category
ORDER BY 総在庫金額 DESC;

実践的なクエリ例

売上分析クエリ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 商品別の注文回数と総販売数量
SELECT 
    p.name AS 商品名,
    p.category AS カテゴリ,
    COUNT(o.order_id) AS 注文回数,
    COALESCE(SUM(o.quantity), 0) AS 総販売数量,
    p.price * COALESCE(SUM(o.quantity), 0) AS 売上金額
FROM products p
LEFT JOIN orders o ON p.product_id = o.product_id
GROUP BY p.product_id, p.name, p.category, p.price
ORDER BY 売上金額 DESC;

在庫アラートクエリ

1
2
3
4
5
6
7
8
9
-- 在庫が20個以下で、かつ価格が10000円以上の商品
SELECT 
    name AS 商品名,
    category AS カテゴリ,
    price AS 価格,
    stock AS 現在在庫
FROM products
WHERE stock <= 20 AND price >= 10000
ORDER BY stock ASC;

日別集計クエリ

1
2
3
4
5
6
7
8
9
-- 日別の注文件数と総数量
SELECT 
    order_date AS 注文日,
    COUNT(*) AS 注文件数,
    SUM(quantity) AS 総数量
FROM orders
GROUP BY order_date
ORDER BY order_date DESC
LIMIT 7;

まとめ

本記事では、PostgreSQLにおけるSELECT文の基本から応用までを解説しました。

SELECT文を使いこなすためのポイントをまとめます。

機能 用途 主なキーワード
基本的なデータ取得 テーブルからカラムを選択 SELECT, FROM
条件指定 必要な行のみ抽出 WHERE, AND, OR, IN, BETWEEN, LIKE
並び替え 結果の順序を制御 ORDER BY, ASC, DESC
件数制限 ページングの実装 LIMIT, OFFSET
集計 データの統計分析 COUNT, SUM, AVG, MAX, MIN
グループ化 カテゴリ別集計 GROUP BY, HAVING
重複排除 ユニークな値の取得 DISTINCT, DISTINCT ON
別名付与 可読性の向上 AS

これらの機能を組み合わせることで、ほとんどのデータ取得要件に対応できます。次のステップとして、JOINを使った複数テーブルの結合やサブクエリについて学ぶことで、より複雑なデータ操作が可能になります。

参考リンク