「データベースを使いたいけれど、何から始めればいいかわからない」「MySQLとPostgreSQLのどちらを選ぶべき?」――データベース初心者が最初にぶつかる壁です。
本記事では、世界中の開発者から高い評価を受けているPostgreSQLについて、インストールから基本的なSQL操作、テーブル設計まで、初心者向けに一通り解説します。実際にコマンドを打ちながら学べるので、この記事を読み終わる頃にはPostgreSQLの基本操作ができるようになるでしょう。
PostgreSQLとは?特徴と選ばれる理由
PostgreSQLは、オープンソースのオブジェクトリレーショナルデータベース管理システム(ORDBMS)です。1986年にカリフォルニア大学バークレー校で始まったPOSTGRESプロジェクトを起源とし、35年以上の歴史を持ちます。
PostgreSQLの主な特徴
- 標準SQL準拠:SQL標準への準拠度が高く、学習した知識が汎用的に使える
- 高度なデータ型:JSON/JSONB、配列、範囲型、幾何学型など豊富なデータ型をサポート
- 拡張性:独自の関数、データ型、演算子を追加可能
- ACID準拠:トランザクションの信頼性が高い
- MVCC:読み取りと書き込みが互いをブロックしない並行制御
- 全文検索:組み込みの全文検索機能(日本語対応も可能)
- コミュニティ:活発な開発コミュニティと豊富なドキュメント
MySQLとの比較
MySQL と PostgreSQL はどちらも人気のあるRDBMSですが、用途によって向き不向きがあります。
PostgreSQLが向いているケース:複雑なクエリ、JSON操作、地理空間データ(PostGIS)、厳密なデータ整合性が必要な業務システム
MySQLが向いているケース:シンプルなCRUD操作が中心のWebアプリ、WordPress、既にMySQL運用のノウハウがある環境
近年では、Supabase、Render、Neon、Vercel PostgresなどのクラウドサービスもPostgreSQLを採用しており、モダンな開発ではPostgreSQLが第一選択肢になりつつあります。
PostgreSQLのインストール
各OS環境でのインストール方法を解説します。
macOSでのインストール
Homebrewを使うのが最も簡単です。
# Homebrewでインストール
brew install postgresql@16
# サービスの開始
brew services start postgresql@16
# バージョン確認
psql --version
# psql (PostgreSQL) 16.x
Ubuntu/Debianでのインストール
# パッケージリストの更新
sudo apt update
# PostgreSQLのインストール
sudo apt install postgresql postgresql-contrib
# サービスの状態確認
sudo systemctl status postgresql
# サービスの開始
sudo systemctl start postgresql
sudo systemctl enable postgresql
Windowsでのインストール
公式サイト(postgresql.org)からインストーラをダウンロードして実行します。インストール時に以下を設定します。
- スーパーユーザー(postgres)のパスワード
- ポート番号(デフォルト:5432)
- ロケール(日本語:Japanese, Japan)
Dockerでのインストール(推奨)
開発環境ではDockerを使うと、環境を汚さずに複数バージョンを管理できます。
# PostgreSQLコンテナの起動
docker run --name my-postgres \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=mypassword \
-e POSTGRES_DB=mydb \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
-d postgres:16
# コンテナ内のpsqlに接続
docker exec -it my-postgres psql -U myuser -d mydb
データベースの作成と接続
PostgreSQLをインストールしたら、まずデータベースを作成しましょう。
psqlコマンドの基本
# PostgreSQLに接続(macOS/Linux)
psql -U postgres
# 特定のデータベースに接続
psql -U postgres -d mydb
# リモートサーバーに接続
psql -h hostname -p 5432 -U username -d dbname
psql内で使えるメタコマンドを覚えておくと便利です。
-- データベース一覧
\l
-- テーブル一覧
\dt
-- テーブル構造の確認
\d テーブル名
-- 現在の接続情報
\conninfo
-- データベースの切り替え
\c データベース名
-- SQLファイルの実行
\i /path/to/file.sql
-- psqlの終了
\q
データベースの作成と削除
-- データベースの作成
CREATE DATABASE blog_app
ENCODING 'UTF8'
LC_COLLATE 'ja_JP.UTF-8'
LC_CTYPE 'ja_JP.UTF-8';
-- データベースの削除(注意!取り消せません)
DROP DATABASE blog_app;
-- データベースが存在する場合のみ削除
DROP DATABASE IF EXISTS blog_app;
ユーザー(ロール)の作成
-- ユーザーの作成
CREATE USER app_user WITH PASSWORD 'secure_password';
-- データベースへの権限付与
GRANT ALL PRIVILEGES ON DATABASE blog_app TO app_user;
-- スキーマへの権限付与
GRANT ALL ON SCHEMA public TO app_user;
-- テーブルへの権限付与(既存テーブル)
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
-- 今後作成されるテーブルにもデフォルトで権限を付与
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
テーブルの作成とデータ型
テーブルはデータを格納する基本的な構造です。用途に合ったデータ型を選ぶことが重要です。
主要なデータ型
-- 数値型
INTEGER -- 整数(-2147483648 〜 2147483647)
BIGINT -- 大きな整数
SERIAL -- 自動増分する整数(主キーに使用)
NUMERIC(10, 2) -- 正確な小数(金額などに使用)
REAL -- 浮動小数点(近似値)
-- 文字列型
VARCHAR(255) -- 可変長文字列(最大長を指定)
TEXT -- 可変長文字列(長さ制限なし)
CHAR(10) -- 固定長文字列
-- 日時型
DATE -- 日付のみ(2026-03-27)
TIME -- 時刻のみ(14:30:00)
TIMESTAMP -- 日時(タイムゾーンなし)
TIMESTAMPTZ -- 日時(タイムゾーンあり、推奨)
-- 真偽値
BOOLEAN -- true / false / null
-- JSON型
JSON -- テキストとして保存
JSONB -- バイナリ形式で保存(インデックス対応、推奨)
-- UUID
UUID -- 一意識別子(gen_random_uuid()で生成)
-- 配列型
INTEGER[] -- 整数の配列
TEXT[] -- テキストの配列
テーブルの作成例
ブログアプリケーションを例に、テーブルを作成してみましょう。
-- ユーザーテーブル
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
display_name VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
bio TEXT,
avatar_url VARCHAR(500),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- カテゴリテーブル
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
slug VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- 記事テーブル
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
slug VARCHAR(255) UNIQUE NOT NULL,
content TEXT NOT NULL,
excerpt VARCHAR(500),
status VARCHAR(20) DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'archived')),
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
author_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- タグテーブル
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL
);
-- 記事とタグの中間テーブル(多対多)
CREATE TABLE post_tags (
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (post_id, tag_id)
);
ここでのポイントは以下の通りです。
SERIAL PRIMARY KEYで自動増分の主キーを設定UNIQUE制約で一意性を保証NOT NULLでNULL値を禁止DEFAULTでデフォルト値を設定CHECK制約で許可する値を制限REFERENCESで外部キー制約を設定ON DELETE CASCADEで親レコード削除時に子も削除
基本的なCRUD操作
CRUD(Create, Read, Update, Delete)は、データベース操作の基本です。
INSERT:データの挿入
-- 1件挿入
INSERT INTO users (username, email, display_name, password_hash)
VALUES ('tanaka', 'tanaka@example.com', '田中太郎', 'hashed_password_here');
-- 複数件を一度に挿入
INSERT INTO categories (name, slug, description) VALUES
('プログラミング', 'programming', 'プログラミングに関する記事'),
('デザイン', 'design', 'UIデザインに関する記事'),
('インフラ', 'infrastructure', 'サーバー・ネットワークに関する記事');
-- 挿入した結果を返す(RETURNING句)
INSERT INTO posts (title, slug, content, author_id, category_id, status)
VALUES (
'PostgreSQL入門',
'postgresql-beginner',
'PostgreSQLの基本を解説します...',
1,
1,
'published'
)
RETURNING id, title, created_at;
RETURNING句はPostgreSQL独自の機能で、INSERTした結果をそのまま取得できるため非常に便利です。
SELECT:データの取得
-- 全件取得
SELECT * FROM users;
-- 特定のカラムのみ取得
SELECT username, email, display_name FROM users;
-- 条件を指定して取得
SELECT * FROM posts WHERE status = 'published';
-- 複数条件(AND / OR)
SELECT * FROM posts
WHERE status = 'published'
AND category_id = 1
AND published_at >= '2026-01-01';
-- 並び替え
SELECT * FROM posts
WHERE status = 'published'
ORDER BY published_at DESC;
-- 件数制限とオフセット(ページネーション)
SELECT * FROM posts
WHERE status = 'published'
ORDER BY published_at DESC
LIMIT 10 OFFSET 0; -- 1ページ目(10件ずつ)
-- 件数カウント
SELECT COUNT(*) FROM posts WHERE status = 'published';
-- グループ化と集計
SELECT category_id, COUNT(*) AS post_count
FROM posts
WHERE status = 'published'
GROUP BY category_id
HAVING COUNT(*) >= 5
ORDER BY post_count DESC;
UPDATE:データの更新
-- 1件更新
UPDATE posts
SET title = '更新されたタイトル',
updated_at = CURRENT_TIMESTAMP
WHERE id = 1;
-- 条件に合致する複数行を更新
UPDATE posts
SET status = 'archived',
updated_at = CURRENT_TIMESTAMP
WHERE status = 'draft'
AND created_at < '2025-01-01';
-- RETURNING句で更新結果を確認
UPDATE posts
SET status = 'published',
published_at = CURRENT_TIMESTAMP,
updated_at = CURRENT_TIMESTAMP
WHERE id = 5
RETURNING id, title, status, published_at;
DELETE:データの削除
-- 1件削除
DELETE FROM posts WHERE id = 10;
-- 条件に合致する複数行を削除
DELETE FROM posts
WHERE status = 'archived'
AND updated_at < '2025-01-01';
-- 削除した行を確認
DELETE FROM posts
WHERE status = 'draft'
AND author_id = 3
RETURNING id, title;
-- テーブルの全データを削除(TRUNCATEの方が高速)
TRUNCATE TABLE post_tags;
JOIN:テーブルの結合
実際のアプリケーションでは、複数のテーブルを結合してデータを取得します。
INNER JOIN
-- 記事と著者情報を結合して取得
SELECT
p.id,
p.title,
p.published_at,
u.display_name AS author_name,
c.name AS category_name
FROM posts p
INNER JOIN users u ON p.author_id = u.id
INNER JOIN categories c ON p.category_id = c.id
WHERE p.status = 'published'
ORDER BY p.published_at DESC;
LEFT JOIN
-- カテゴリが未設定の記事も含めて取得
SELECT
p.id,
p.title,
COALESCE(c.name, '未分類') AS category_name
FROM posts p
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.status = 'published';
多対多のJOIN
-- 記事に紐づくタグを取得
SELECT
p.id,
p.title,
ARRAY_AGG(t.name) AS tags
FROM posts p
LEFT JOIN post_tags pt ON p.id = pt.post_id
LEFT JOIN tags t ON pt.tag_id = t.id
WHERE p.status = 'published'
GROUP BY p.id, p.title
ORDER BY p.id;
ARRAY_AGGはPostgreSQL独自の集約関数で、複数行の値を配列にまとめます。
インデックスの基本
インデックスは、データ検索を高速化するための仕組みです。書籍の索引と同じ役割を果たします。
インデックスの作成
-- 基本的なインデックス
CREATE INDEX idx_posts_status ON posts(status);
-- 複合インデックス
CREATE INDEX idx_posts_status_published ON posts(status, published_at DESC);
-- 部分インデックス(条件付き)
CREATE INDEX idx_posts_published ON posts(published_at DESC)
WHERE status = 'published';
-- ユニークインデックス
CREATE UNIQUE INDEX idx_users_email ON users(email);
-- GINインデックス(JSONB・配列・全文検索向け)
CREATE INDEX idx_posts_metadata ON posts USING GIN(metadata);
インデックスの確認と削除
-- テーブルのインデックス一覧
\di
-- 特定テーブルのインデックスを確認
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'posts';
-- インデックスの削除
DROP INDEX idx_posts_status;
-- クエリの実行計画を確認(インデックスが使われているか確認)
EXPLAIN ANALYZE
SELECT * FROM posts
WHERE status = 'published'
ORDER BY published_at DESC
LIMIT 10;
EXPLAIN ANALYZEでクエリの実行計画を確認し、Index Scanが使われていればインデックスが効いています。Seq Scan(テーブル全走査)になっている場合は、インデックスの追加を検討しましょう。
実践的なSQL:アプリケーション開発でよく使うクエリ
実際のアプリケーション開発で頻出するクエリパターンを紹介します。
UPSERT(INSERT or UPDATE)
-- 存在しなければINSERT、存在すればUPDATE
INSERT INTO users (username, email, display_name, password_hash)
VALUES ('tanaka', 'tanaka@example.com', '田中太郎', 'new_hash')
ON CONFLICT (username)
DO UPDATE SET
display_name = EXCLUDED.display_name,
updated_at = CURRENT_TIMESTAMP;
CTE(WITH句)による複雑なクエリの整理
-- 人気記事ランキング(仮想的なアクセス数テーブルと結合)
WITH popular_posts AS (
SELECT
post_id,
COUNT(*) AS view_count
FROM page_views
WHERE viewed_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY post_id
ORDER BY view_count DESC
LIMIT 10
)
SELECT
p.title,
p.slug,
u.display_name AS author,
pp.view_count
FROM popular_posts pp
JOIN posts p ON pp.post_id = p.id
JOIN users u ON p.author_id = u.id;
ウィンドウ関数
-- カテゴリごとの記事ランキング
SELECT
title,
category_id,
published_at,
ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY published_at DESC) AS rank
FROM posts
WHERE status = 'published';
JSONBの操作
-- JSONBカラムを持つテーブル
ALTER TABLE posts ADD COLUMN metadata JSONB DEFAULT '{}';
-- JSONBデータの挿入
UPDATE posts
SET metadata = '{"reading_time": 5, "tags": ["postgresql", "beginner"]}'
WHERE id = 1;
-- JSONBデータの検索
SELECT * FROM posts
WHERE metadata->>'reading_time' = '5';
-- JSONBの配列に含まれるかチェック
SELECT * FROM posts
WHERE metadata->'tags' ? 'postgresql';
-- JSONBの部分更新
UPDATE posts
SET metadata = metadata || '{"featured": true}'
WHERE id = 1;
まとめ:PostgreSQL学習の次のステップ
本記事では、PostgreSQLの基本を一通り解説しました。
- インストール:macOS、Linux、Windows、Dockerでのセットアップ
- データベース・テーブル作成:データ型の選択と制約の設定
- CRUD操作:INSERT、SELECT、UPDATE、DELETEの基本
- JOIN:INNER JOIN、LEFT JOINによるテーブル結合
- インデックス:検索パフォーマンスの改善
- 実践SQL:UPSERT、CTE、ウィンドウ関数、JSONB操作
PostgreSQLの基本を理解したら、次のステップとして以下のトピックに進むとよいでしょう。
- トランザクション:BEGIN / COMMIT / ROLLBACKによるデータの一貫性確保
- ビューとマテリアライズドビュー:複雑なクエリのカプセル化
- 関数とトリガー:PL/pgSQLによるサーバーサイドロジック
- パフォーマンスチューニング:EXPLAIN ANALYZEとインデックス最適化
- バックアップとリストア:pg_dump / pg_restoreの運用
PostgreSQLは奥が深いデータベースですが、基本のSQL操作さえ身につけば、多くのアプリケーション開発に対応できます。まずはDockerで環境を立ち上げ、実際に手を動かしながら覚えていくことをおすすめします。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像