PostgreSQL: PostgreSQLのSELECTクエリの基本
最終更新:2026-08-26
1. 学習目標
SELECTを使用してカラム、式、エイリアスをクエリする方法DISTINCTで重複行を除去する方法WHEREでデータを絞り込む方法ORDER BYでソートする方法(PostgreSQL独自のNULLS FIRST/LASTを含む)LIMIT/OFFSETとFETCH FIRSTでページネーションを行う方法
2. ストーリー
BobはECプラットフォームのデータベースエンジニアです。このプラットフォームでは1日あたり約10,000件の注文が発生し、過去30日間で100,000件のレコードが蓄積されています。運用チームから以下の依頼がありました。
- 過去30日間の全注文を金額の高い順にクエリする
- 1ページ50件でページング対応する
- キャンセルされた注文を除外する
- 重複する顧客IDを表示から除外する
BobはSELECT、WHERE、ORDER BY、LIMIT/OFFSET、そしてDISTINCTを使ってこのタスクを完了する必要があります。
3. 概念: SELECTの基本
(1) SELECT構文の概要
SELECT column1, column2, ...
FROM table_name
[WHERE condition]
[ORDER BY column [ASC|DESC] [NULLS FIRST|NULLS LAST]]
[LIMIT count [OFFSET start]];
SELECTは最もよく使われるSQL文で、1つまたは複数のテーブルからデータを取得��るために使用します。
(2) 全カラムと特定カラム
| 書き方 | 説明 | パフォーマンス | 推奨シーン |
|---|---|---|---|
SELECT * |
全カラムを返す | 劣る—冗長なデータを転送 | テーブル構造の簡易確認 |
SELECT col1, col2 |
指定カラムのみ返す | 優れる—I/Oが少ない | 本番クエリ |
▶ サンプル: 全カラムのクエリ
SELECT * FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル: 特定カラムのクエリ
SELECT order_id, customer_id, total_amount, order_date
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) カラムエイリアス
ASキーワード(省略可能)を使用して、カラムや式に別名を付け、結果を読みやすくします。
| 使用法 | 例 | 説明 |
|---|---|---|
明示的なAS |
price * qty AS subtotal |
推奨—明確 |
AS省略 |
price * qty subtotal |
有効だが可読性が低い |
| 引用符付きエイリアス | total_amount AS "Order Total" |
スペースや大文字小文字を含む場合に必要 |
▶ サンプル: カラムエイリアスの使用
SELECT
order_id,
total_amount AS amount,
order_date AS "Order Date",
total_amount * 0.08 AS tax
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(4) 式と計算カラム
SELECTはカラムの読み取りだけでなく、算術式や関数呼び出しなどを記述して計算カラムを生成することもできます。
▶ サンプル: 計算カラム
SELECT
product_name,
unit_price,
unit_price * 1.1 AS price_with_tax,
ROUND(unit_price * 1.1, 2) AS rounded_price
FROM products;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
4. 概念: DISTINCTによる重複除去
(1) 基本的な重複除去
DISTINCTは結果セットから重複行を除去します。ユニークな値をカウントする際によく使われます。
▶ サンプル: ユニークな顧客IDのクエリ
SELECT DISTINCT customer_id
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 複数カラムの重複除去
DISTINCTは指定されたすべてのカラムの組み合わせに対して適用され、単一カラムに対してではありません。
| 文 | 重複除去の範囲 |
|---|---|
SELECT DISTINCT a |
aで重複除去 |
SELECT DISTINCT a, b |
(a, b)の組み合わせで重複除去 |
SELECT DISTINCT ON (a) a, b |
PostgreSQLのみ: aで重複除去、グループごとに最初の行を保持 |
▶ サンプル: 複数カラムの組み合わせ重複除去
SELECT DISTINCT customer_id, order_status
FROM orders;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) DISTINCT ON: PostgreSQL独自の構文
DISTINCT ON (expr)は指定された式で重複除去し、各グループの最初の行を返します(ORDER BYと組み合わせてどの行を返すか決定します)。
▶ サンプル: 各顧客の最新注文
SELECT DISTINCT ON (customer_id)
customer_id, order_id, order_date, total_amount
FROM orders
ORDER BY customer_id, order_date DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
5. 概念: WHEREによる絞り込み
(1) 基本的な比較演算子
| 演算子 | 意味 | 例 |
|---|---|---|
= |
等しい | order_status = 'completed' |
<> または != |
等しくない | total_amount <> 0 |
> |
より大きい | total_amount > 100 |
< |
より小さい | total_amount < 50 |
>= |
以上 | total_amount >= 100 |
<= |
以下 | total_amount <= 500 |
▶ サンプル: 完了した注文の絞り込み
SELECT order_id, customer_id, total_amount
FROM orders
WHERE order_status = 'completed';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 日付絞り込み
日付比較はECサイトで最も一般的な絞り込みニーズの一つです。
▶ サンプル: 過去30日間の注文
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) 複数条件の組み合わせ
AND、OR、NOTを使用して条件を組み合わせます。優先順位: NOT > AND > OR。括弧で明示的に制御してください。
▶ サンプル: 複合条件絞り込み
SELECT order_id, total_amount, order_status
FROM orders
WHERE order_status = 'completed'
AND total_amount > 500
AND order_date >= '2025-01-01';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
6. 概念: ORDER BYによるソート
(1) 基本的なソート
ORDER BYのデフォルトは昇順(ASC)です。降順(DESC)も指定できます。カラム名、エイリアス、またはカラム位置によるソートをサポートしています。
| ソート方法 | 構文 | 説明 |
|---|---|---|
| 昇順 | ORDER BY col ASC |
デフォルト、ASCは省略可能 |
| 降順 | ORDER BY col DESC |
高い順 |
| エイリアス | ORDER BY amount DESC |
SELECTのエイリアスを使用 |
| 位置指定 | ORDER BY 3 DESC |
SELECTリストの3番目のカラム(非推奨) |
▶ サンプル: 金額降順ソート
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 複数カラムソート
複数カラムソートでは、最初のカラムでソートし、同値の場合は2番目のカラムでソートします。
▶ サンプル: ステータス順、次に金額順
SELECT order_id, order_status, total_amount
FROM orders
ORDER BY order_status ASC, total_amount DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) NULLS FIRST / NULLS LAST: PostgreSQLの機能
PostgreSQLはデフォルトでNULLを最大値として扱います(昇順では最後、降順では最初)。NULLS FIRSTとNULLS LASTでNULLの位置を明示的に制御できます。
| ソート | NULLのデフォルト位置 | 調整可能 |
|---|---|---|
ASC |
NULLは末尾 | NULLS FIRSTでNULLを先頭に |
DESC |
NULLは先頭 | NULLS LASTでNULLを末尾に |
▶ サンプル: NULLS FIRST/LASTの制御
SELECT product_name, discount_rate
FROM products
ORDER BY discount_rate DESC NULLS LAST;
Output:
count
-------
5
(1 row)
7. 概念: LIMITとFETCH FIRSTによるページネーション
(1) LIMIT / OFFSET
LIMITは返却行数を制限し、OFFSETは指定された行数をスキップしま��。組み合わせてページネーションを��装します。
▶ サンプル: 1ページ50件、3ページ目
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_status = 'completed'
ORDER BY total_amount DESC
LIMIT 50 OFFSET 100;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) FETCH FIRST: SQL標準構文
PostgreSQLはSQL標準のFETCH FIRST構文もサポートしており、LIMITと同等の意味を持ちます。
| 構文 | 同等の書き方 | 説明 |
|---|---|---|
LIMIT 10 |
FETCH FIRST 10 ROWS ONLY |
標準形式 |
LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH FIRST 10 ROWS ONLY |
標準のページネーション形式 |
▶ サンプル: FETCH FIRSTによるページネーション
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC
OFFSET 100 ROWS
FETCH FIRST 50 ROWS ONLY;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) ページネーションの計算式
| パラメータ | 計算方法 |
|---|---|
ページ page |
1から始まる |
ページサイズ size |
例: 50 |
OFFSET |
(page - 1) * size |
LIMIT |
size |
▶ サンプル: 動的ページネーション(疑似コード)
page=3
size=50
offset=$(( (page - 1) * size ))
psql -c "SELECT order_id, total_amount FROM orders ORDER BY total_amount DESC LIMIT $size OFFSET $offset;"
Output:
# psql command executed successfully
8. フローチャート: SELECTクエリの実行順序
flowchart TD
A[FROM テーブル] --> B[WHERE 絞り込み]
B --> C[SELECT カラム / 式]
C --> D[DISTINCT 重複除去]
D --> E[ORDER BY ソート]
E --> F[LIMIT / OFFSET ページネーション]
F --> G[結果セット]
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#e8f5e9
style D fill:#f3e5f5
style E fill:#fce4ec
style F fill:#e0f2f1
style G fill:#c8e6c9
9. 総合サンプル
Bobは運用チームの要件を完了しました。過去30日間の完了注文を金額降順でページネーションし、ユニークな顧客数をレポートします。
-- Step 1: 過去30日間の完了注文、金額降順でページネーション
SELECT
order_id,
customer_id,
total_amount,
total_amount * 0.08 AS tax_amount,
order_date AS "Order Date",
order_status
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY total_amount DESC NULLS LAST
LIMIT 50 OFFSET 0;
-- Step 2: 該当注文のユニーク顧客数をカウント
SELECT COUNT(DISTINCT customer_id) AS unique_customers
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days';
-- Step 3: 各顧客の最新注文(DISTINCT ON)
SELECT DISTINCT ON (customer_id)
customer_id,
order_id,
total_amount,
order_date
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY customer_id, total_amount DESC;
-- Step 4: 計算済み合計(価格+税)のトップ10注文
SELECT
order_id,
customer_id,
total_amount,
ROUND(total_amount * 1.08, 2) AS total_with_tax,
order_date
FROM orders
WHERE order_status = 'completed'
AND order_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY total_with_tax DESC
FETCH FIRST 10 ROWS ONLY;
❓ よくある質問
📖 まとめ
SELECTはSQLクエリの基礎であり、カラム名、式、エイリアスをサポートしていますDISTINCTは重複行を除去します。DISTINCT ONはPostgreSQL独自の構文ですWHEREは比較演算子で行を絞り込み、AND/OR/NOTの組み合わせをサポートしますORDER BYはASC/DESCとPostgreSQL独自のNULLS FIRST/LASTをサポートしますLIMIT/OFFSETとFETCH FIRSTはページネーションを実装します。大きなオフセットではキーセットページネーションが必要です- SQLの論理的実行順序: FROM → WHERE → SELECT → DISTINCT → ORDER BY → LIMIT
📝 練習問題
-
⭐
productsからproduct_nameとunit_priceを選択し、価格の昇順でソートして最初の10行のみ返すクエリを書いてください。 -
⭐⭐
ordersから過去7日間の注文を選択し、total_amount > 200で絞り込み、order_date DESCでソートし、FETCH FIRST 20 ROWS ONLYを使用するクエリを書いてください。 -
⭐⭐⭐
DISTINCT ONを使用して各customer_idの最高金額注文をクエリし(ヒント:ORDER BY customer_id, total_amount DESC)、それらの顧客のうち最大単一注文が1,000 USDを超える顧客数をカウントしてください。