PostgreSQL入門|インストールから基本SQLまで初心者向けに完全解説

kento_morota 22分で読めます

「データベースを使いたいけれど、何から始めればいいかわからない」「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で環境を立ち上げ、実際に手を動かしながら覚えていくことをおすすめします。

#PostgreSQL#SQL#データベース
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

起業準備に役立つ情報、もっとありますよ。

まずは話だけ聞いてもらう