PostgreSQL: PostgreSQLのSELECTクエリの基本

最終更新:2026-08-26

1. 学習目標


2. ストーリー

BobはECプラットフォームのデータベースエンジニアです。このプラットフォームでは1日あたり約10,000件の注文が発生し、過去30日間で100,000件のレコードが蓄積されています。運用チームから以下の依頼がありました。

  1. 過去30日間の全注文を金額の高い順にクエリする
  2. 1ページ50件でページング対応する
  3. キャンセルされた注文を除外する
  4. 重複する顧客IDを表示から除外する

BobはSELECTWHEREORDER BYLIMIT/OFFSET、そしてDISTINCTを使ってこのタスクを完了する必要があります。


3. 概念: SELECTの基本

(1) SELECT構文の概要

SQL
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が少ない 本番クエリ

▶ サンプル: 全カラムのクエリ

SQL
SELECT * FROM orders;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ サンプル: 特定カラムのクエリ

SQL
SELECT order_id, customer_id, total_amount, order_date
FROM orders;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) カラムエイリアス

ASキーワード(省略可能)を使用して、カラムや式に別名を付け、結果を読みやすくします。

使用法 説明
明示的なAS price * qty AS subtotal 推奨—明確
AS省略 price * qty subtotal 有効だが可読性が低い
引用符付きエイリアス total_amount AS "Order Total" スペースや大文字小文字を含む場合に必要

▶ サンプル: カラムエイリアスの使用

SQL
SELECT
  order_id,
  total_amount AS amount,
  order_date AS "Order Date",
  total_amount * 0.08 AS tax
FROM orders;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(4) 式と計算カラム

SELECTはカラムの読み取りだけでなく、算術式や関数呼び出しなどを記述して計算カラムを生成することもできます。

▶ サンプル: 計算カラム

SQL
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:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

4. 概念: DISTINCTによる重複除去

(1) 基本的な重複除去

DISTINCTは結果セットから重複行を除去します。ユニークな値をカウントする際によく使われます。

▶ サンプル: ユニークな顧客IDのクエリ

SQL
SELECT DISTINCT customer_id
FROM orders;

Output:

TEXT 📖 参照専用
 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で重複除去、グループごとに最初の行を保持

▶ サンプル: 複数カラムの組み合わせ重複除去

SQL
SELECT DISTINCT customer_id, order_status
FROM orders;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) DISTINCT ON: PostgreSQL独自の構文

DISTINCT ON (expr)は指定された式で重複除去し、各グループの最初の行を返します(ORDER BYと組み合わせてどの行を返すか決定します)。

▶ サンプル: 各顧客の最新注文

SQL
SELECT DISTINCT ON (customer_id)
  customer_id, order_id, order_date, total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

Output:

TEXT 📖 参照専用
 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

▶ サンプル: 完了した注文の絞り込み

SQL
SELECT order_id, customer_id, total_amount
FROM orders
WHERE order_status = 'completed';

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) 日付絞り込み

日付比較はECサイトで最も一般的な絞り込みニーズの一つです。

▶ サンプル: 過去30日間の注文

SQL
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days';

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) 複数条件の組み合わせ

ANDORNOTを使用して条件を組み合わせます。優先順位: NOT > AND > OR。括弧で明示的に制御してください。

▶ サンプル: 複合条件絞り込み

SQL
SELECT order_id, total_amount, order_status
FROM orders
WHERE order_status = 'completed'
  AND total_amount > 500
  AND order_date >= '2025-01-01';

Output:

TEXT 📖 参照専用
 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番目のカラム(非推奨)

▶ サンプル: 金額降順ソート

SQL
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) 複数カラムソート

複数カラムソートでは、最初のカラムでソートし、同値の場合は2番目のカラムでソートします。

▶ サンプル: ステータス順、次に金額順

SQL
SELECT order_id, order_status, total_amount
FROM orders
ORDER BY order_status ASC, total_amount DESC;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) NULLS FIRST / NULLS LAST: PostgreSQLの機能

PostgreSQLはデフォルトでNULLを最大値として扱います(昇順では最後、降順では最初)。NULLS FIRSTNULLS LASTでNULLの位置を明示的に制御できます。

ソート NULLのデフォルト位置 調整可能
ASC NULLは末尾 NULLS FIRSTでNULLを先頭に
DESC NULLは先頭 NULLS LASTでNULLを末尾に

▶ サンプル: NULLS FIRST/LASTの制御

SQL
SELECT product_name, discount_rate
FROM products
ORDER BY discount_rate DESC NULLS LAST;

Output:

TEXT 📖 参照専用
 count 
-------
     5
(1 row)

7. 概念: LIMITとFETCH FIRSTによるページネーション

(1) LIMIT / OFFSET

LIMITは返却行数を制限し、OFFSETは指定された行数をスキップしま��。組み合わせてページネーションを��装します。

▶ サンプル: 1ページ50件、3ページ目

SQL
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:

TEXT 📖 参照専用
 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によるページネーション

SQL
SELECT order_id, customer_id, total_amount
FROM orders
ORDER BY total_amount DESC
OFFSET 100 ROWS
FETCH FIRST 50 ROWS ONLY;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) ページネーションの計算式

パラメータ 計算方法
ページ page 1から始まる
ページサイズ size 例: 50
OFFSET (page - 1) * size
LIMIT size

▶ サンプル: 動的ページネーション(疑似コード)

BASH
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:

TEXT 📖 参照専用
# psql command executed successfully

8. フローチャート: SELECTクエリの実行順序

100%
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日間の完了注文を金額降順でページネーションし、ユニークな顧客数をレポートします。

SQL
-- 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;

❓ よくある質問

Q 重複除去におけるDISTINCTとGROUP BYの違いは何ですか?
A DISTINCTは重複行を除去するだけです。GROUP BYは集計も行えます。集計なしで重複除去のみ行う場合はDISTINCTの方が直感的で��。集計が必要な場合はGROUP BYを使用してください。
Q 大きなオフセットでのLIMIT OFFSETページネーションのパフォーマンスはどうですか?
A OFFSETは最初のN行をスキップする必要があるため、オフセットが大きくなるほど遅くなります。大規模データセットではキーセットページネーション(ソートキーでWHERE絞り込み)を推奨します。例: WHERE order_id > last_seen_id LIMIT 50。
Q NULLS FIRST/LASTは標準SQLですか?
A いいえ、PostgreSQLの拡張機能です。MySQLやSQL Serverではサポートされていません。CASEやCOALESCEでエミュレートします。
Q DISTINCT ONをORDER BYなしで使用できますか?
A 構文的には可能ですが、ORDER BYがないとグループごとにどの行が保持されるかは未定義です。予測可能な結果を得るには必ずORDER BYと組み合わせてください。
Q カラムエイリアスをWHEREで使用できますか?
A いいえ。SQLの実行順序ではWHEREがSELECTより先に実行されるため、エイリアスはまだ存在しません。ただしエイリアスはORDER BYで使用できます。ORDER BYはSELECTの後に実行されるためです。
Q FETCH FIRSTとLIMITは完全に同等ですか?
A 機能的には同じです。FETCH FIRSTはSQL:2008標準構文で、LIMITはPostgreSQLの伝統的な構文です。新しいコードではFETCH FIRSTを使うと移植性が向上します。
Q ORDER BYでSELECTリストにないカラムを参照できますか?
A はい。PostgreSQLではORDER BYでSELECTにないカラムを参照できますが、DISTINCTやGROUP BYを使用する場合はソートカラムがSELECTまたはGROUP BYに含まれている必要があります。

📖 まとめ


📝 練習問題

  1. productsからproduct_nameunit_priceを選択し、価格の昇順でソートして最初の10行のみ返すクエリを書いてください。

  2. ⭐⭐ ordersから過去7日間の注文を選択し、total_amount > 200で絞り込み、order_date DESCでソートし、FETCH FIRST 20 ROWS ONLYを使用するクエリを書いてください。

  3. ⭐⭐⭐ DISTINCT ONを使用して各customer_idの最高金額注文をクエリし(ヒント: ORDER BY customer_id, total_amount DESC)、それらの顧客のうち最大単一注文が1,000 USDを超える顧客数をカウントしてください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%