プログラミング言語を整える
中級PostgreSQL を整える。
= 本物の PostgreSQL をブラウザの中で動かして、SQL の書き方と PostgreSQL らしさを学ぶ
PostgreSQL が初めての方は、先に そもそも PostgreSQL とは・どんな場面で使うか をどうぞ。
PostgreSQLとは
標準 SQL に忠実で機能の多いオープンソースのリレーショナルデータベースで、Web サービスから業務システムまで幅広く使われています。
| 誕生 | 1986年にカリフォルニア大学バークレー校で始まった研究プロジェクト POSTGRES が元で、SQL に対応した Postgres95 を経て、1996年に PostgreSQL と名前を改めました |
|---|---|
| 作った人・組織 | Michael Stonebraker ら(カリフォルニア大学バークレー校)。現在は世界中の開発者が集まる PostgreSQL Global Development Group が開発しています |
| 種類 | リレーショナルデータベース(RDB) |
| ライセンス | PostgreSQL License(BSD や MIT に近い寛容なライセンス) |
| 得意なこと | 標準 SQL への準拠度が高く、トランザクション・ウィンドウ関数・JSON(jsonb)・拡張機能(PostGIS など)まで1つの処理系で扱えます。 |
| 手元で動かすには | psql -U postgres |
日本での使われ方
- 日本でも早くから使われてきたデータベースで、日本 PostgreSQL ユーザ会(JPUG)が日本語の情報やイベントを提供しています。
- Web サービスのバックエンドで、アプリケーションのデータを保存する先としてよく選ばれます。
- 商用データベースからの移行先として、企業の業務システムで採用されることがあります。
- NTT グループの開発者など、日本からも本体の開発に参加している人がいます。
使えるクラウドサービス
- Amazon RDS for PostgreSQL:AWS が提供するマネージドサービスで、バックアップやパッチ適用を任せられます。
- Amazon Aurora PostgreSQL 互換エディション:AWS が独自に作ったストレージの上で、PostgreSQL と互換のある操作ができるデータベースです。
- Google Cloud SQL for PostgreSQL:Google Cloud が提供するマネージドサービスです。
- Azure Database for PostgreSQL:Microsoft Azure が提供するマネージドサービスです。
- Supabase:PostgreSQL を中心に、認証や API などをまとめて提供するサービスです。
- Neon:サーバーレスで使える PostgreSQL のサービスで、データベースを枝分かれ(ブランチ)させる機能があります。
研修・学習での位置づけ
PostgreSQL は無料で入手でき、標準 SQL に近い書き方をするため、SQL の基本を学ぶ研修の教材として使われます。エラーメッセージが具体的で、どこが間違っているかを確かめながら学べます。ここで覚えた SELECT・JOIN・GROUP BY の書き方は、ほかの RDB でもほぼそのまま通用します。
関わる資格
- OSS-DB技術者認定資格 OSS-DB Silver(LPI-Japan。データベースの運用管理と SQL の基礎)
- OSS-DB技術者認定資格 OSS-DB Gold(LPI-Japan。性能監視やチューニングなどの上級の内容)
ブラウザでPostgreSQLを動かす
本物の PostgreSQL(PGlite)をこのページの中で動かします。書いた SQL もデータも端末の外には出ません。最初の1回だけ、処理系(約16MB)をこのサイトから読み込みます。
-- 準備のデータ(products と orders)が入った状態で流れます SELECT name, price FROM products WHERE price >= 100 ORDER BY price DESC;
CREATE TABLE products ( id integer PRIMARY KEY, name text NOT NULL, category text NOT NULL, price integer NOT NULL ); CREATE TABLE orders ( id integer PRIMARY KEY, product_id integer NOT NULL REFERENCES products (id), qty integer NOT NULL, ordered_on date NOT NULL ); INSERT INTO products (id, name, category, price) VALUES (1, 'りんご', '果物', 150), (2, 'みかん', '果物', 80), (3, '牛乳', '飲み物', 200), (4, 'お茶', '飲み物', 120), (5, '食パン', 'パン', 250); INSERT INTO orders (id, product_id, qty, ordered_on) VALUES (1, 1, 3, '2026-10-01'), (2, 3, 1, '2026-10-01'), (3, 2, 5, '2026-10-02'), (4, 1, 2, '2026-10-03'), (5, 4, 4, '2026-10-03');
結果
「実行」を押すと、準備のデータ(下の「準備(全お題共通)」)を入れてから、エディタのコードを流します。
この練習場でできること・できないこと
- 動いているのは本物の PostgreSQL 18.6 を WebAssembly にした PGlite です。結果もエラーのメッセージも本物と同じです。
- データベースはこのページを開いている間だけ続き、閉じると消えます。「最初からやり直す」で準備のデータを入れ直せます。
- 1人で使うデータベースなので、ユーザーや権限、ほかの接続との同時実行(ロックの待ち合わせ)は試せません。拡張機能も入れていません。
- 5秒を超える問い合わせは止めます(データベースは空に戻ります)。
そもそも PostgreSQL とは
機能が豊富で、無料で商用にも使える、オープンソースのリレーショナルデータベース。
PostgreSQL(ポストグレス・キュー・エル)は、SQL で操作するサーバー型のリレーショナルデータベース(RDB)です。オープンソースで公開されていて、無料で商用のサービスにも使えます。長い歴史があり、データの正しさを守る仕組みが丁寧に作られています。
MySQL とは同じ SQL を使う仲間で、細かい書き方や得意なことが違います。SQLite のようにアプリへ組み込むタイプではなく、サーバーとして動かして、ネットワーク越しに大勢が同時に使う前提です。JSON の扱い、複雑な集計、拡張機能で機能を足せる点が特徴です。
どこで・どんな場面で使うか
-
Web サービスの本体のデータベース
会員・注文・投稿などを保存する土台として使われます。
-
業務システム
お金や在庫など、数字がずれては困るデータを扱うシステムで選ばれます。
-
データ分析・地図データ
複雑な集計に強く、拡張機能の PostGIS を入れると地図・位置情報も扱えます。
-
クラウドのマネージドサービス
主要なクラウドが、運用を任せられる PostgreSQL 版を用意しています。自分でサーバーを管理しなくても使えます。
-
求人・資格での位置づけ
バックエンドやインフラの募集で名前が出ます。PostgreSQL を中心にした OSS-DB という資格もあります。
向いている場面・向いていない場面
- 向いている場面:データの整合性が大切な業務データ、複数の表を結び付ける複雑な問い合わせ、JSON も一緒に持ちたいアプリ。
- 向いていない場面:画面ごとに何度も読む値を超高速で返したい用途(キャッシュなら Redis など)や、一人用のアプリに小さく組み込みたい用途(SQLite など)。
- 迷ったら:まず SQL の基本を覚えれば、MySQL など他の RDB にもそのまま活きます。
上の練習場では、本物の PostgreSQL がブラウザの中で動きます。下のお題の SQL を流して、結果を確かめてみてください。
6つのお題をPostgreSQLで書く
PostgreSQL・MySQL・DuckDB・MongoDB・Redis の5ページで、同じ商品と注文のデータに同じ6つのお題を並べています。SQL の方言の違いや、SQL を使わないデータベースでの考え方の違いがそのまま比べられます。
表示している結果とエラーのメッセージは、PostgreSQL 18.6(PGlite 0.5.11) で実際に流して、一字一句同じになることを確かめたものです。上の練習場で動いているのも同じ処理系です。結果の表は、見比べやすいように全ページで psql に寄せた同じ形にそろえています(NULL は NULL と表示)。
準備(全お題共通)表を作ってデータを入れるコード。お題2〜6・クイズ・エラーの例は、これを流した後に続けて流した結果です。
CREATE TABLE products ( id integer PRIMARY KEY, name text NOT NULL, category text NOT NULL, price integer NOT NULL ); CREATE TABLE orders ( id integer PRIMARY KEY, product_id integer NOT NULL REFERENCES products (id), qty integer NOT NULL, ordered_on date NOT NULL ); INSERT INTO products (id, name, category, price) VALUES (1, 'りんご', '果物', 150), (2, 'みかん', '果物', 80), (3, '牛乳', '飲み物', 200), (4, 'お茶', '飲み物', 120), (5, '食パン', 'パン', 250); INSERT INTO orders (id, product_id, qty, ordered_on) VALUES (1, 1, 3, '2026-10-01'), (2, 3, 1, '2026-10-01'), (3, 2, 5, '2026-10-02'), (4, 1, 2, '2026-10-03'), (5, 4, 4, '2026-10-03');
SQL(PostgreSQL)
CREATE TABLE products ( id integer PRIMARY KEY, name text NOT NULL, category text NOT NULL, price integer NOT NULL ); CREATE TABLE orders ( id integer PRIMARY KEY, product_id integer NOT NULL REFERENCES products (id), qty integer NOT NULL, ordered_on date NOT NULL ); INSERT INTO products (id, name, category, price) VALUES (1, 'りんご', '果物', 150), (2, 'みかん', '果物', 80), (3, '牛乳', '飲み物', 200), (4, 'お茶', '飲み物', 120), (5, '食パン', 'パン', 250); INSERT INTO orders (id, product_id, qty, ordered_on) VALUES (1, 1, 3, '2026-10-01'), (2, 3, 1, '2026-10-01'), (3, 2, 5, '2026-10-02'), (4, 1, 2, '2026-10-03'), (5, 4, 4, '2026-10-03'); SELECT * FROM products ORDER BY id;
結果
id | name | category | price ----+--------+----------+------- 1 | りんご | 果物 | 150 2 | みかん | 果物 | 80 3 | 牛乳 | 飲み物 | 200 4 | お茶 | 飲み物 | 120 5 | 食パン | パン | 250 (5 行)
PostgreSQL では文字列の型に text をよく使います。長さの上限を付けない text と varchar(n) に速さの違いはありません。id を自動で振りたいときは integer GENERATED ALWAYS AS IDENTITY と書けますが、ここでは結果をそろえるため番号を自分で入れています。
SQL(PostgreSQL)
SELECT name, category, price
FROM products
WHERE price >= 100
AND category IN ('果物', '飲み物')
ORDER BY price DESC; 結果
name | category | price --------+----------+------- 牛乳 | 飲み物 | 200 りんご | 果物 | 150 お茶 | 飲み物 | 120 (3 行)
文字列は一重引用符('果物')で囲みます。二重引用符("name")は列や表の名前を表す記号で、文字列には使えません。文字列の比較は大文字と小文字を区別するため、区別しないで探すときは ILIKE を使います。
SQL(PostgreSQL)
SELECT category,
COUNT(*) AS items,
ROUND(AVG(price), 1) AS avg_price
FROM products
GROUP BY category
ORDER BY items DESC, category; 結果
category | items | avg_price ----------+-------+----------- 果物 | 2 | 115.0 飲み物 | 2 | 160.0 パン | 1 | 250.0 (3 行)
integer の列に AVG を使うと、結果は numeric(桁を正確に持つ数値の型)になります。ROUND(値, 桁数) は numeric を受け取る関数なので、そのまま小数1桁に丸められます。COUNT(*) の型は bigint です。
SQL(PostgreSQL)
SELECT o.id, p.name, o.qty, p.price * o.qty AS amount FROM orders AS o JOIN products AS p ON p.id = o.product_id ORDER BY o.id;
結果
id | name | qty | amount ----+--------+-----+-------- 1 | りんご | 3 | 450 2 | 牛乳 | 1 | 200 3 | みかん | 5 | 400 4 | りんご | 2 | 300 5 | お茶 | 4 | 480 (5 行)
JOIN だけを書くと INNER JOIN(両方の表に対応する行があるものだけ)になります。AS o のように表に短い別名を付けると、列名の前に付ける表の名前を短く書けます。
SQL(PostgreSQL)
SELECT id, name, price FROM products ORDER BY price DESC LIMIT 2 OFFSET 2;
結果
id | name | price ----+--------+------- 1 | りんご | 150 4 | お茶 | 120 (2 行)
LIMIT は取る件数、OFFSET は読み飛ばす件数です。標準 SQL の書き方 OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY も使え、結果は同じです。ORDER BY を付けないと行の順番は決まらないため、ページ分けでは必ず並べ替えます。
SQL(PostgreSQL)
INSERT INTO products (id, name, category, price) VALUES (2, 'みかん', '果物', 90), (6, 'バナナ', '果物', 100) ON CONFLICT (id) DO UPDATE SET price = EXCLUDED.price; SELECT * FROM products ORDER BY id;
結果
id | name | category | price ----+--------+----------+------- 1 | りんご | 果物 | 150 2 | みかん | 果物 | 90 3 | 牛乳 | 飲み物 | 200 4 | お茶 | 飲み物 | 120 5 | 食パン | パン | 250 6 | バナナ | 果物 | 100 (6 行)
ON CONFLICT (id) は「id がすでにあったら」という意味で、EXCLUDED は入れようとした行を指します。何もしないなら DO NOTHING と書きます。INSERT の最後に RETURNING * を付けると、追加・更新した行をその場で受け取れます。
出力当てクイズ
準備のデータを入れた後に流したときの、最後の SQL の結果を選んでください。選ぶと正解と理由が出ます(答えはどこにも送られず、記録もしません)。
問1
SELECT 7 / 2;
画面に出るのは?
integer どうしの割り算は integer になり、小数点以下は切り捨てられます。3.5 を得たいときは 7.0 / 2 や 7::numeric / 2 のように、片方を小数を持てる型にします。
問2
SELECT 'abc' || NULL;
画面に出るのは?
|| は文字列をつなぐ演算子ですが、どちらかが NULL だと結果も NULL になります。NULL を空の文字列として扱いたいときは concat('abc', NULL) か COALESCE を使います。
問3
SELECT NULL = NULL;
画面に出るのは?
NULL は「値が分からない」ことを表すため、NULL どうしを = で比べても結果は真でも偽でもなく NULL です。NULL かどうかは IS NULL で確かめます。なお PostgreSQL の真偽値は、真が t、偽が f と表示されます。
問4
SELECT '2026-10-31'::date + 1;
画面に出るのは?
:: は型を変える PostgreSQL の書き方です。date に整数を足すと、その日数だけ進んだ date になり、月末をまたいでも正しい日付になります。
よくあるエラーと直し方
書き始めの人が出会いやすいエラーです。メッセージは英語のまま出るので、どの言葉を手がかりにすればよいかもあわせて載せています。
syntax error(キーワードの書き間違い)
エラーになるコード
SELEC * FROM products;
画面に出るメッセージ(抜粋)
ERROR: syntax error at or near "SELEC"
何が起きているか:SELECT の T が抜けているため、PostgreSQL が文の始まりを読み取れませんでした。at or near のあとに、読めなくなった場所の語が出ます。
直し方:エラーに出た語(ここでは SELEC)の周りを見直し、SELECT と正しく書きます。
relation does not exist(表が見つからない)
エラーになるコード
SELECT * FROM product;
画面に出るメッセージ(抜粋)
ERROR: relation "product" does not exist
何が起きているか:表の名前は products なのに、s を付け忘れて product と書いたため、その名前の表が見つかりませんでした。relation は表やビューのことです。
直し方:表の名前を products に直します。psql なら \dt で表の一覧を確かめられます。名前を "Products" のように二重引用符で囲んで作った表は、大文字と小文字を区別するので、使うときも同じく囲んで書きます。
must appear in the GROUP BY clause(集計していない列)
エラーになるコード
SELECT category, name, COUNT(*) FROM products GROUP BY category;
画面に出るメッセージ(抜粋)
ERROR: column "products.name" must appear in the GROUP BY clause or be used in an aggregate function
何が起きているか:category ごとにまとめると、1つのグループに name が複数あります。どの name を出せばよいか決まらないため、エラーになりました。
直し方:name を GROUP BY に加えるか、string_agg(name, '、') や MIN(name) のような集計関数で1つの値にまとめます。
duplicate key value(主キーの重複)
エラーになるコード
INSERT INTO products (id, name, category, price) VALUES (1, 'ぶどう', '果物', 400);
画面に出るメッセージ(抜粋)
ERROR: duplicate key value violates unique constraint "products_pkey"
何が起きているか:id は主キー(PRIMARY KEY)なので同じ値を2回入れられません。id が 1 の行はすでにあるため、追加できませんでした。products_pkey は主キーに自動で付いた制約の名前です。
直し方:まだ使っていない id(ここでは 6 など)を入れます。あれば更新したいときは、お題「あれば更新・無ければ追加」の ON CONFLICT を使います。
ほかのデータベースの解説
- MySQL 1995年・リレーショナルデータベース
- DuckDB 2019年・組み込み型の分析用
- MongoDB 2009年・ドキュメント指向データベース
- Redis 2009年・インメモリのキーバリューストア
5つのデータベースと SQLite を並べた比較は データベースを整える にあります。 書いたコードはどこにも送られません(このページに入力欄はクイズの選択だけで、選んだ答えも記録しません)。
このツールについて
標準 SQL に忠実で機能の多いオープンソースのリレーショナルデータベースで、Web サービスから業務システムまで幅広く使われています。
PostgreSQL の概要と、同じ6つのお題・出力当てクイズ・よくあるエラー。ブラウザの中で本物の PostgreSQL 18 が動く練習場つき。
主な機能
- 概要(誕生・作った人・種類・ライセンス)と日本での使われ方・クラウドサービス・研修と資格
- 同じ商品と注文のデータで6つのお題(MySQL・DuckDB・MongoDB・Redis の同じお題へ移れる)
- 出力当てクイズ4問(正解と理由)
- よくあるエラー4件(実際のメッセージ・原因・直し方)
- 本物の PostgreSQL 18.6(PGlite)が動く練習場・お題とクイズをそのまま実行
- 処理系はサイト内から配信・外部通信なし・送信ゼロ
よくある質問
Q. 練習場では本物の PostgreSQL が動いているのですか?
A. はい。PostgreSQL 18.6 を WebAssembly にした PGlite 0.5.11 を、このページの中(Web Worker)で動かしています。結果もエラーのメッセージも本物と同じです。1人で使うデータベースなので、ユーザーや権限、同時に接続したときの動きは試せません。
Q. 書いた SQL やデータはどこかに送られますか?
A. 送られません。処理系(約16MB)はこのサイト自身から読み込み、外部の配信元(CDN)は使っていません。データベースはページを開いている間だけ端末のメモリにあり、閉じると消えます。
Q. SELECT 7 / 2 が 3 になるのはなぜですか?
A. PostgreSQL では整数どうしの割り算の結果も整数になり、小数点以下が切り捨てられるためです。3.5 が欲しいときは 7.0 / 2 や 7::numeric / 2 のように片方を小数の型にします。MySQL では 3.5000、DuckDB では 3.5 になるので、データベースを変えるときに気をつけたい違いです。
入力値はURLの「#」以降に入るためサーバーには送信されません。リンクを開くと同じ状態を復元します。
続けて整える
データの時点:各ページに記載の版(各プロジェクトの公式リリース)