psql
psql: はじめてのガイド — PostgreSQLコマンドラインクライアントでローカル開発をマスターする
本記事は、Apple M4 Max搭載のMacBook Proで、PostgreSQL公式の対話型ターミナルpsqlを使いこなすためのステップバイステップガイドです。この記事を最後まで進めると、PostgreSQLのインストール確認、psqlの基本メタコマンドの習得、データベースとテーブルの作成、psqlの生産性向上設定、ローカル開発ワークフローの構築が完了します。
1. 前提条件
ハードウェアとOS
本ガイドでは、Apple Silicon(特にM4 Maxチップ)上のmacOSを使用していることを前提としています。PostgreSQLはHomebrew経由でネイティブARM64ビルドを提供しており、Rosetta 2変換なしで動作します。
Homebrew
Homebrewがインストールされている必要があります。確認:
brew --version
期待される出力:
Homebrew 4.x.x
PostgreSQL
PostgreSQLがすでにインストールされている場合、インストールを確認します:
psql --version
期待される出力:
psql (PostgreSQL) 17.x
サーバーが起動しているか確認します:
brew services list | grep postgresql
期待される出力:
postgresql@17 started shogo ~/Library/LaunchAgents/homebrew.mxcl.postgresql@17.plist
起動していない場合:
brew services start postgresql@17
アーキテクチャの理解
PostgreSQLはクライアント・サーバーモデルを使用しています:
- サーバー(
postgres):データの管理、クエリの処理、接続の処理を行うデータベースエンジン。Macのバックグラウンドサービスとして動作します。 - クライアント(
psql):サーバーに接続し、SQLコマンドを送信し、結果を表示する対話型ターミナル。
psqlを実行すると、ローカル接続の場合はUnixドメインソケット(/tmp/.s.PGSQL.5432)、リモート接続の場合はTCP/IPでPostgreSQLサーバーに接続します。
2. 最初の接続
デフォルトデータベースへの接続
HomebrewでPostgreSQLをインストールすると、macOSのユーザー名と同じ名前のデフォルトデータベースとスーパーユーザーロールが作成されます。以下で接続します:
psql
これは以下と同等です:
psql -h localhost -p 5432 -U $(whoami) -d $(whoami)
期待される出力:
psql (17.x)
Type "help" for help.
shogo=#
=# プロンプトはスーパーユーザーとして接続していることを示します。=> プロンプトは一般ユーザーを示します。
psqlとは?
psqlはPostgreSQLの公式対話型ターミナルです。以下の機能を提供します:
- SQL実行:接続先データベースに対して任意のSQLステートメントを実行。
- メタコマンド:バックスラッシュコマンド(例:
\dt、\l)でSQLを書かずにデータベースオブジェクトを調査。 - タブ補完:テーブル名、カラム名、SQLキーワード、メタコマンドを自動補完。
- 履歴:コマンド履歴が
~/.psql_historyにセッション間で保持。 - スクリプティング:SQLファイルの実行とフォーマット済み出力の自動化。
接続パラメータ
psqlは複数の方法で接続先を指定できます:
| フラグ | 環境変数 | 説明 | デフォルト |
|---|---|---|---|
-h | PGHOST | サーバーのホスト名 | ローカルソケット |
-p | PGPORT | ポート番号 | 5432 |
-U | PGUSER | ユーザー名 | OSユーザー名 |
-d | PGDATABASE | データベース名 | ユーザー名 |
-W | PGPASSWORD | パスワード入力を強制 | (パスワードなし) |
接続URIも使用できます:
psql "postgresql://user:password@localhost:5432/dbname"
3. 基本メタコマンド
メタコマンドはバックスラッシュ(\)で始まり、サーバーに送信されずにpsql自体で処理されます。
ナビゲーションと情報
\l -- すべてのデータベースを一覧表示
\c dbname -- 別のデータベースに接続
\conninfo -- 現在の接続情報を表示
\conninfo を実行:
You are connected to database "shogo" as user "shogo" via socket in "/tmp" at port "5432".
データベースオブジェクトの調査
\dt -- 現在のスキーマのテーブル一覧
\dt+ -- テーブル一覧(サイズと説明付き)
\d tablename -- テーブルの詳細(カラム、型、インデックス)
\d+ tablename -- ストレージと統計情報付き詳細
\di -- インデックス一覧
\dv -- ビュー一覧
\df -- 関数一覧
\ds -- シーケンス一覧
\dn -- スキーマ一覧
\du -- ロール/ユーザー一覧
クエリ出力の制御
\x -- 拡張表示の切り替え(縦方向出力、幅広テーブルに最適)
\x auto -- 幅に応じて通常表示と拡張表示を自動切り替え
\timing -- クエリ実行時間の表示切り替え
\pset format -- 出力形式の設定(aligned, unaligned, csv, html, latex)
\pset border 2 -- 罫線スタイルの設定(0, 1, 2)
ヘルプと履歴
\? -- すべてのメタコマンド一覧
\h -- SQLコマンド一覧
\h CREATE TABLE -- 特定のSQLコマンドのヘルプ
\s -- コマンド履歴の表示
\g -- 最後のクエリを再実行
\e -- 最後のクエリを$EDITORで開く
入出力
\i filename.sql -- ファイルからSQLを実行
\o output.txt -- クエリ出力をファイルに送信
\o -- ファイルへの出力を停止
\copy -- クライアント側COPY(CSV のインポート/エクスポート)
4. 開発用データベースの作成
ロールとデータベースの作成
ローカル開発では、デフォルトのスーパーユーザーを使用するのではなく、専用のロールとデータベースを作成します:
CREATE ROLE devuser WITH LOGIN PASSWORD 'devpass';
CREATE DATABASE myapp OWNER devuser;
確認:
\l myapp
期待される出力:
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ...
-------+---------+----------+-----------------+------------+------------+----
myapp | devuser | UTF8 | libc | en_US.UTF-8| en_US.UTF-8| ...
(1 row)
新しいロールとして接続
psql -U devuser -d myapp
またはpsql内から:
\c myapp devuser
スキーマの作成
スキーマはデータベース内の論理的な名前空間を提供します:
CREATE SCHEMA app;
SET search_path TO app, public;
5. テーブルの作成とデータの操作
テーブルの作成
CREATE TABLE app.users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE app.orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES app.users(id) ON DELETE CASCADE,
product VARCHAR(255) NOT NULL,
quantity INTEGER NOT NULL CHECK (quantity > 0),
price NUMERIC(10, 2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_orders_user_id ON app.orders(user_id);
CREATE INDEX idx_orders_status ON app.orders(status);
テーブルの確認
\d app.users
期待される出力:
Table "app.users"
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+-----------------------------------
id | integer | | not null | nextval('app.users_id_seq'::regclass)
email | character varying(255) | | not null |
name | character varying(100) | | not null |
created_at | timestamp with time zone | | | now()
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"users_email_key" UNIQUE CONSTRAINT, btree (email)
Referenced by:
TABLE "app.orders" CONSTRAINT "orders_user_id_fkey" FOREIGN KEY (user_id) REFERENCES app.users(id) ON DELETE CASCADE
データの挿入
INSERT INTO app.users (email, name) VALUES
('alice@example.com', 'Alice Johnson'),
('bob@example.com', 'Bob Smith'),
('charlie@example.com', 'Charlie Brown');
INSERT INTO app.orders (user_id, product, quantity, price, status) VALUES
(1, 'Widget', 5, 19.99, 'completed'),
(1, 'Gadget', 2, 49.99, 'pending'),
(2, 'Widget', 10, 19.99, 'completed'),
(3, 'Doohickey', 1, 99.99, 'shipped');
データのクエリ
SELECT u.name, o.product, o.quantity, o.price, o.status
FROM app.users u
JOIN app.orders o ON u.id = o.user_id
ORDER BY u.name, o.created_at;
期待される出力:
name | product | quantity | price | status
---------------+-----------+----------+--------+-----------
Alice Johnson | Widget | 5 | 19.99 | completed
Alice Johnson | Gadget | 2 | 49.99 | pending
Bob Smith | Widget | 10 | 19.99 | completed
Charlie Brown | Doohickey | 1 | 99.99 | shipped
(4 rows)
6. 生産性向上機能
拡張表示の切り替え
カラム数が多いテーブルには拡張表示を使用します:
\x auto
SELECT * FROM app.users WHERE id = 1;
期待される出力:
-[ RECORD 1 ]-------------------------
id | 1
email | alice@example.com
name | Alice Johnson
created_at | 2026-04-13 10:30:00+09
タイミング
パフォーマンスをプロファイルするためにクエリタイミングを有効化:
\timing
SELECT count(*) FROM app.orders;
期待される出力:
count
-------
4
(1 row)
Time: 0.892 ms
エディタでの編集
最後のクエリをデフォルトエディタで開きます:
\e
$EDITOR(デフォルトはvi)で最後のクエリが開きます。保存して閉じると実行されます。
SQLファイルの実行
psql -U devuser -d myapp -f schema.sql
またはpsql内から:
\i schema.sql
CSVのエクスポートとインポート
クエリ結果をCSVにエクスポート:
\copy (SELECT * FROM app.users) TO 'users.csv' WITH CSV HEADER
CSVデータのインポート:
\copy app.users (email, name) FROM 'new_users.csv' WITH CSV HEADER
7. .psqlrcの設定
~/.psqlrc ファイルを作成して、毎回のセッションでpsqlの動作をカスタマイズします:
cat > ~/.psqlrc << 'EOF'
-- 起動時のクワイエットモード
\set QUIET 1
-- クエリタイミングを表示
\timing
-- 拡張表示の自動切り替え
\x auto
-- NULLの表示を改善
\pset null '(null)'
-- 詳細なエラーレポート
\set VERBOSITY verbose
-- 履歴を長く保持
\set HISTSIZE 5000
-- 重複コマンドを記録しない
\set HISTCONTROL ignoredups
-- カスタムプロンプト: database[user]=#
\set PROMPT1 '%[%033[1;32m%]%/%[%033[0m%][%n]%R%# '
\set PROMPT2 '%R%# '
-- SQLキーワードを大文字で補完
\set COMP_KEYWORD_CASE upper
-- クワイエットモードをオフ
\set QUIET 0
\echo 'psql config loaded.'
EOF
psqlを再起動して変更を確認:
psql -U devuser -d myapp
期待されるプロンプト:
psql config loaded.
myapp[devuser]=>
8. ローカル開発で役立つSQLパターン
アクティブな接続の確認
SELECT pid, usename, datname, state, query_start, query
FROM pg_stat_activity
WHERE datname = 'myapp';
テーブルサイズの確認
SELECT
schemaname || '.' || relname AS table,
pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
pg_size_pretty(pg_relation_size(relid)) AS data_size,
pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS index_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
クエリパフォーマンスの分析
EXPLAIN ANALYZE
SELECT u.name, count(o.id) AS order_count
FROM app.users u
LEFT JOIN app.orders o ON u.id = o.user_id
GROUP BY u.name
ORDER BY order_count DESC;
シーケンスのリセット
ALTER SEQUENCE app.users_id_seq RESTART WITH 1;
全テーブルの削除と再作成(開発環境リセット)
DROP SCHEMA app CASCADE;
CREATE SCHEMA app;
SET search_path TO app, public;
-- マイグレーションファイルを再実行
\i schema.sql
トランザクションのワークフロー
BEGIN;
INSERT INTO app.users (email, name) VALUES ('dave@example.com', 'Dave Wilson');
-- 結果を確認
SELECT * FROM app.users WHERE email = 'dave@example.com';
-- 問題なければコミット
COMMIT;
-- 取り消す場合
-- ROLLBACK;
9. アプリケーションコードからの接続
接続文字列の形式
アプリケーションは接続文字列を使用してPostgreSQLに接続します:
postgresql://devuser:devpass@localhost:5432/myapp
Python (psycopg2)
pip3 install psycopg2-binary
import psycopg2
conn = psycopg2.connect(
host="localhost",
port=5432,
user="devuser",
password="devpass",
dbname="myapp"
)
cur = conn.cursor()
cur.execute("SELECT name, email FROM app.users ORDER BY name")
for row in cur.fetchall():
print(f"{row[0]} <{row[1]}>")
cur.close()
conn.close()
Node.js (pg)
npm install pg
const { Client } = require('pg');
const client = new Client({
connectionString: 'postgresql://devuser:devpass@localhost:5432/myapp'
});
async function main() {
await client.connect();
const res = await client.query('SELECT name, email FROM app.users ORDER BY name');
res.rows.forEach(row => console.log(`${row.name} <${row.email}>`));
await client.end();
}
main();
10. トラブルシューティング
"connection refused"
PostgreSQLサーバーが起動していません:
brew services start postgresql@17
"role does not exist"
指定したロールが存在しません。利用可能なロールを確認:
psql -c "\du"
"database does not exist"
利用可能なデータベースを一覧表示:
psql -c "\l"
"permission denied for schema"
ロールに権限を付与:
GRANT ALL ON SCHEMA app TO devuser;
GRANT ALL ON ALL TABLES IN SCHEMA app TO devuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT ALL ON TABLES TO devuser;
サーバーログの確認
PostgreSQLのログは以下に格納されています:
tail -f /opt/homebrew/var/log/postgresql@17.log
11. クリーンアップ
開発用データベースとロールの削除
-- まずスーパーユーザーとして接続
DROP DATABASE myapp;
DROP ROLE devuser;
.psqlrcの削除(任意)
rm ~/.psqlrc
PostgreSQLの停止
brew services stop postgresql@17
PostgreSQLのアンインストール(任意)
brew uninstall postgresql@17
rm -rf /opt/homebrew/var/postgresql@17
主要概念のまとめ
| 概念 | 説明 |
|---|---|
| psql | SQL実行とデータベースオブジェクト調査のためのPostgreSQL公式対話型ターミナル |
| メタコマンド | psql自体が処理するバックスラッシュコマンド(\dt、\l、\d) |
| ロール | ログインしてオブジェクトを所有できるデータベースID(「ユーザー」と「グループ」の概念を統合) |
| スキーマ | テーブルなどのオブジェクトを整理するためのデータベース内の論理的な名前空間 |
| SERIAL | 主キー用の自動増分整数型(PostgreSQL固有の省略記法) |
| TIMESTAMPTZ | タイムゾーン付きタイムスタンプ。本番環境ではTIMESTAMPより推奨 |
| search_path | テーブル名がスキーマ修飾されていない場合に検索するスキーマを決定 |
| .psqlrc | psqlの起動設定ファイル。毎セッション実行される |
| \copy | サーバーのファイルシステムへのアクセスなしでCSVをインポート/エクスポートするクライアント側COPYコマンド |
| EXPLAIN ANALYZE | 実行時統計情報付きの実際のクエリ実行計画を表示 |
次のステップ
- pg_dump / pg_restore:開発スナップショットのためのデータベースのバックアップと復元。
- 拡張機能:
pgcrypto、uuid-ossp、pg_trgmなどの拡張機能をインストールして高度な機能を利用。 - pgAdminまたはDBeaver:ビジュアルなスキーマ管理のためにpsqlと並行してGUIツールを使用。
- マイグレーション:FlywayまたはAlembicでバージョン管理されたスキーママイグレーションを設定。
- コネクションプーリング:本番に近いローカル環境でPgBouncerを使用したコネクションプーリング。
- レプリケーション:ローカルで本番のリードレプリカ構成をシミュレートするストリーミングレプリケーションの設定。