下記を参照してインストールしてください。
# Docker $ sudo docker run --name my-postgres -e POSTGRES_PASSWORD=my-password -d postgres $ sudo docker exec -it my-postgres psql -h localhost -U postgres -d postgres $ sudo docker exec -it my-postgres /bin/bash $ psql -h localhost -U postgres -d postgres # Ubuntu 22.04 sudo apt -y install lsb-release wget gnupg sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt \ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo apt-get update sudo apt-get -y install postgresql sudo -u postgres psql # RHEL9/AlmaLinux9/Rocky Linux9 sudo dnf install -y \ https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo dnf -qy module disable postgresql sudo dnf install -y postgresql16-server sudo /usr/pgsql-16/bin/postgresql-16-setup initdb sudo systemctl enable postgresql-16 sudo systemctl start postgresql-16 sudo -u postgres psql
psql は PostgreSQL に接続するクライアントコマンドです。-h で接続先サーバを、-U でユーザIDを、-d でデータベース名を指定します。ユーザID を省略すると OS のログインユーザID でアクセスします。
$ psql -h localhost -U postgres -d postgres psql (16.1) Type "help" for help. postgres=#
終了するには \quit を使用します。
postgres=# \quit
PostgreSQL でデータを管理するには SQL(Structured Query Language) を用います。SQL では様々なコマンド群がサポートされいますが、下記に大別されます。
DDL(Data Definition Language) はデータベース、スキーマ、テーブルなどを作成・削除するコマンド群で CREATE, DROP, ALTER などがあります。
DML(Data Manipulation Language) はデータを参照・挿入・削除・更新するコマンド群で SELECT, INSERT, DELETE, UPDATE などがあります。
DCL(Data Control Language) はユーザ権限を設定したり、トランザクションを制御するコマンド群で GRANT, REVOKE, BEGIN, COMMIT, ROLLBACK などがあります。
\du SELECT USENAME FROM pg_user;
CREATE USER yamada;
DROP USER yamada;
ALTER USER yamada WITH PASSWORD 'yamada123';
PostgreSQL ではユーザとロールはほぼ同じものと考えて構いません。ユーザを作成すると同名のロールも作成されます。ロールを作成しても同名のユーザが作成されます。CREATE USER と CREATE ROLE の違いは、CREATE USER が作成したユーザ(ロール)に LOGIN 権限がつくのに対して、CREATE ROLE で作成したユーザ(ロール) には NOLOGIN 権限となりログインできない点が異なります。
\du SELECT USENAME FROM pg_roles;
CREATE ROLE myrole;
DROP ROLE myrole;
PostgreSQL ではひとつのデータベースクラスタ中に複数のデータベースを持つことができます。デフォルトでは下記のデータベースが作成されています。
postgres template0 template1
postgres はデフォルトで使用されるデータベースです。create database で他のデータベースを作成することもできます。template1 は create database でデータベースを作成する際にテンプレートとして使用されるデータベースです。template1 にテーブルを作成しておくと、新規データベース作成時にも同じテーブルが作成されます。template0 は、template1 に何かテーブルが作成された状態で空のデータベースを作成したい時に使用します。create database の --template オプションに指定します。
\l SELECT * FROM pg_database;
CREATE DATABASE mydb;
DROP DATABASE mydb;
ALTER DATABASE mydb OWNER TO yamada;
postgres=# \c mydb; mydb=#
ひとつのデータベースの中に複数のスキーマを作成することができます。デフォルトでは public という名前のスキーマが作成されています。スキーマ名を省略した場合は public スキーマが使用されます。
public
\dn \dn+ SELECT * FROM information_schema.schemata;
CREATE SCHEMA myschema;
DROP SCHEMA myschema;
ALTER SCHEMA myschema OWNER TO yamada;
SELECT current_schema(); SHOW search_path;
スキーマは検索順序で指定します。下記の様に指定すると、テーブルを探す際に schema1 → schema2 の順序で探します。
SET search_path TO schema1, schema2;
ひとつのスキーマの中に複数のテーブルを作成することができます。
\dt \dt myschema.* SELECT * FROM pg_tables;
CREATE TABLE mytable ( id text, name text ); CREATE TABLE myschema.mytable ( id text, name text );
DROP TABLE mytable; DROP TABLE myschema.mytable;
\d mytable; \d myschema.mytable;
ALTER TABLE mytable ADD new_column text;
ALTER TABLE mytable RENAME old_name TO new_name;
ALTER TABLE mytable DROP column_name;
TRUNCATE mytable;
CREATE VIEW は他のテーブルの値を SELECT した結果を仮想的なテーブル(ビュー)として定義します。
CREATE TABLE users (id text, name text); CREATE TABLE points (id text, date date, point integer); CREATE VIEW points_view (id, name, dare, point) AS SELECT p.id, u.name, p.date, p.point FROM points AS p LEFT JOIN users AS u ON u.id = p.id; SELECT * FROM points_view;
DTOP VIEW points_view;
\dv SELECT schemaname, viewname, viewowner FROM pg_views;
\ev points_view
SELECT * FROM users;
WHERE は検索条件を指定します。
SELECT * FROM users WHERE user_id = 'U12345';
ORDER BY はソート条件を指定します。
SELECT * FROM users ORDER BY user_id DESC;
LIMIT は表示する最大件数を指定します。
SELECT * FROM users LIMIT 10;
OFFSET は検索を開始する位置を指定します。
SELECT * FROM users ORDER BY user_id OFFSET 10 LIMIT 10;
count() はレコードの件数を表示します。
SELECT count(*) FROM users;
distinct() は重複したデータを排除した結果を出力します。
SELECT distinct(id) FROM users;
GROUP BY は指定したカラムでグルーピングし、個数(count)、平均値(avg)、最大値(max)、最小値(min)などの集約関数の結果を出力します。
SELECT id, avg(point) AS average FROM points GROUP BY id;
WHERE は GROUP BY による集約を行う前に抽出を行いますが、HAVING は GROUP BY による集約を行った後に抽出を行います。
SELECT id, avg(point) AS average FROM points GROUP BY id HAVING id = 'U001';
INSERT INTO users ( user_id, user_name ) VALUES ( 'U12345', 'Yamada' );
UPDATE users SET user_name = 'Tanaka' WHERE user_id = 'U12345';
DELETE FROM users WHERE user_id = 'U12345';
該当するレコードがあれば更新(UPDATE)し、なければ挿入(INSERT)する仕組みで、属に UPSERT と呼ばれます。重複を判断するカラムは PRIMARY KEY か UNIQUE 制約のカラムである必要があります。
CREATE TABLE users (id text, name text, PRIMARY KEY(id)); INSERT INTO users VALUES ('U123', 'Yamada'); INSERT INTO users VALUES ('U123', 'Tanaka') ON CONFLICT(id) DO UPDATE SET name = 'Tanaka';
PostgreSQL 15 からは MERGE 文も使用できるようになりました。
MERGE INTO users AS a USING (VALUES ('U123', 'Suzuki')) AS b(id, name) ON a.id = b.id WHEN MATCHED THEN UPDATE SET name = b.name WHEN NOT MATCHED THEN INSERT (id, name) VALUES (b.id, b.name;
JOIN は複数のテーブルを結合します。下記の結合方法があります。FROM に指定するのが左テーブル、JOIN で指定するのが右テーブルと呼ばれます。確認のために下記のような左テーブル(points)、右テーブル(users)を用意します。
CREATE TABLE users (id text, name text); CREATE TABLE points (id text, point integer); INSERT INTO users VALUES ('U001', 'Yamada'), ('U002', 'Tanaka'), ('U003', 'Suzuki'); INSERT INTO points VALUES ('U001', 82), ('U002', 63), ('U004', 58); SELECT * FROM points; -- 左テーブルには U001, U002, U004 がある id | point ------+------- U001 | 82 U002 | 63 U004 | 58 SELECT * FROM users; -- 右テーブルには U001, U002, U003 がある id | name ------+-------- U001 | Yamada U002 | Tanaka U003 | Suzuki
JOIN (=INNER JOIN)は内部結合と呼ばれ、左テーブル・右テーブル両方にあるレコードが出力されます。
SELECT * FROM points AS p JOIN users AS u ON u.id = p.id; id | point | id | name ------+-------+------+-------- U001 | 82 | U001 | Yamada U002 | 63 | U002 | Tanaka
LEFT JOIN(=LEFT OUTER JOIN)は左外部結合と呼ばれ、左テーブル(points)にあるレコードのみが出力されます。
SELECT * FROM points AS p LEFT JOIN users AS u ON u.id = p.id; id | point | id | name ------+-------+------+-------- U001 | 82 | U001 | Yamada U002 | 63 | U002 | Tanaka U004 | 58 | |
RIGHT JOIN(=RIGHT OUTER JOIN)は右外部結合と呼ばれ、右テーブル(users)にあるレコードのみが出力されます。
SELECT * FROM points AS p RIGHT JOIN users AS u ON u.id = p.id; id | point | id | name ------+-------+------+-------- U001 | 82 | U001 | Yamada U002 | 63 | U002 | Tanaka | | U003 | Suzuki
FULL JOIN(=FULL OUTER JOIN)は右外部結合と呼ばれ、左テーブル(points)または右テーブル(users)にあるレコードがすべて出力されます。
SELECT * FROM points AS p FULL JOIN users AS u ON u.id = p.id; id | point | id | name ------+-------+------+-------- U001 | 82 | U001 | Yamada U002 | 63 | U002 | Tanaka | | U003 | Suzuki U004 | 58 | |
CROSS JOIN は左テーブルと右テーブルの完全な組み合わせを出力します。
SELECT * FROM points CROSS JOIN users; id | point | id | name ------+-------+------+-------- U001 | 82 | U001 | Yamada U001 | 82 | U002 | Tanaka U001 | 82 | U003 | Suzuki U002 | 63 | U001 | Yamada U002 | 63 | U002 | Tanaka U002 | 63 | U003 | Suzuki U004 | 58 | U001 | Yamada U004 | 58 | U002 | Tanaka U004 | 58 | U003 | Suzuki
UNION を用いると複数の SELECT の結果を縦方向に結合することができます。
CREATE TABLE users_A (id text, name text); CREATE TABLE users_B (id text, name text); INSERT INTO users_A VALUES ('U001', 'Yamada'), ('U002', 'Tanaka'); INSERT INTO users_B VALUES ('U003', 'Sasaki'), ('U004', 'Suzuki'); SELECT * FROM users_A UNION SELECT * FROM users_B; id | name ------+-------- U001 | Yamada U002 | Tanaka U003 | Sasaki U004 | Suzuki
例えば、Aさんのポイントを 10ポイント Bさんに移す場合、(1)Aさんのポイントを10減算する、(2)Bさんのポイントを10加算する という処理を行います。この際、(1) の処理を行ったあと (2) の処理を行う間は不整合な状態にあります。(1) と (2) の両方を行ってからまとめてデータベースに反映するためにはトランザクションを用います。BEGIN はトランザクションを開始し、COMMIT はトランザクションの結果をまとめてデータベースに書き込みます。
DROP TABLE IF EXISTS users; CREATE TABLE users ( id text, name text, point int, PRIMARY KEY(id) ); INSERT INTO users VALUES ('U001', 'Yamada', 100); INSERT INTO users VALUES ('U002', 'Tanaka', 100); BEGIN; UPDATE users SET point = point - 10 WHERE id = 'U001'; -- (1) UPDATE users SET point = point + 10 WHERE id = 'U002'; -- (2) COMMIT; SELECT * FROM users;
ROLLBACK はトランザクションを破棄してトランザクション開始前の状態に戻します。
BEGIN; UPDATE users SET point = point - 10 WHERE id = 'U001'; -- (1) UPDATE users SET point = point + 10 WHERE id = 'U002'; -- (2) ROLLBACK;
SELECT FOR UPDATE または SELECT FOR SHARE を用いて SELECT した対象のレコードをロックすることができます。ロックしている間、他のプロセスは禁止された操作がブロックされます。(NOWAIT をつけた場合はエラー)
BEGIN; SELECT * FROM users WHERE id = 'U001' FOR UPDATE; UPDATE users SET point = 100 WHERE id = 'U001'; COMMIT;
テーブル単位でロックするには LOCK を用います。
BEGIN; LOCK users; : COMMIT;
ACCESS SHARE から ACCESS EXCLUSIVE まで様々なロックモードがサポートされています。詳細は 「明示的ロック」を参照してください。
LOCK users IN ACCESS EXCLUSIVE MODE
ユーザ(ロール)に対してテーブルに対する権限を付与します。権限には SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER を指定できます。
GRANT ALL PRIVILEGES ON users TO yamada; GRANT SELECT, INSERT, UPDATE, DELETE ON users TO yamada;
スキーマに含まれるすべてのテーブルに対する権限を与えるには次のようにします。
GRANT SELECT ON ALL TABLES IN SCHEMA myschema TO yamada;
権限を削除するには REVOKE を用います。
REVOKE SELECT ON users FROM yamada; REVOKE ALL PRIVILEGES ON users FROM yamada; REVOKE SELECT ON ALL TABLES IN SCHEMA public FROM yamada;
手続きをまとめて実行するプロシージャをサポートしています。開発言語としては PL/pgSQL、PL/Tcl、PL/Perl、PL/Python の他、JavaScript(v8)、C言語などもサポートされています。
下記の様にプロシージャを定義することができます。
CREATE PROCEDURE add_user(id text, name text) -- add_userプロシージャを定義 LANGUAGE plpgsql -- 言語を指定 AS $$ -- $$までの間がプロシージャ定義 BEGIN INSERT INTO users VALUES (id, name); END $$; -- プロシージャ定義の終わり
プロシージャは下記の様に呼び出すことができます。
CREATE TABLE users (id text, name text); CALL add_user('U001', 'Yamada'); CALL add_user('U002', 'Tanaka'); CALL add_user('U003', 'Suzuki'); SELECT * FROM users;
\df
DROP PROCEDURE add_user;
下記の様に関数を定義することができます。プロシージャと似ていますが、戻り値を返すことができる点が異なります。
CREATE FUNCTION add_func(x int, y int) -- add()関数を定義 RETURNS integer -- 戻り値の型はinteger LANGUAGE plpgsql -- 言語を指定 AS $$ -- $$ までの間が関数定義 BEGIN RETURN x + y; -- x と y の和を返す END $$; -- 関数定義の終わり
関数は下記の様にして呼び出すことができます。
SELECT add_func(3, 5); -- 関数を呼び出し
\df
\sf add_func
\ef add_func
DROP FUNCTION add_func;
デバッグなどの目的で、関数の中の変数の値を表示したいときは RAISE を使用します。レベルには DEBUG, LOG, INFO, NOTICE, WARNING, EXCEPTION があります。
RAISE NOTICE 'xx=%, yy=%, zz=%', xx, yy, zz;
テーブルにデータが INSERT されたなどのタイミングで関数を呼び出すこともできます。下記の例では x と y に値が INSERT されたら、その合計を z に入れる例を示しています。
CREATE TABLE numbers (id serial, x int, y int, z int); CREATE FUNCTION add_func() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE xx integer; yy integer; zz integer; BEGIN xx := NEW.x; yy := NEW.y; zz := xx + yy; UPDATE numbers SET z = zz WHERE id = NEW.id; RETURN NEW; END $$; CREATE TRIGGER add_func_trigger AFTER INSERT ON numbers FOR EACH ROW EXECUTE FUNCTION add_func(); INSERT INTO numbers (x, y) VALUES (5, 3); SELECT * FROM numbers;
SELECT * FROM pg_trigger; SELECT * FROM information_schema.triggers;
DROP TRIGGER add_func_trigger ON numbers;
クエリーバッファは直前に実行した SELECT などのコマンドです。クエリーバッファを表示したり、外部エディタで編集して再実行することができます。
\p
\e
\s
\x を実行すると SELECT の結果が表形式ではなく、レコード毎表示に切り替わります。もう一度 \x を実行すると表形式モードに戻ります。
SELECT * FROM users; id | name ------+-------- U001 | Yamada \x SELECT * FROM users; -[ RECORD 1 ] id | U001 name | Yamada
\x auto としておくと、表示する内容によって 表形式・レコード形式 を自動的に切り替えてくれます。
\x auto
\t を実行すると SELECT の結果でヘッダの表示を抑制します。もう一度 \t を実行するとヘッダ表示モードに戻ります。
\t SELECT * FROM users; U001 | Yamada
\a を実行すると SELECT の結果でカラムの一番長い文字数に合わせてアライメントしなくなります。もう一度 \a を実行するとアライメントモードに戻ります。
\a SELECT * FROM users; id|name U001|Yamada
\a でアライメント無しモードにして \pset fieldsep をカンマ(,)に指定すると、カラムをカンマ(,)区切りで表示することができます。
\a \pset fieldsep ',' SELECT * FROM users; id,name U001,Tanaka
レコード数が多いと SELECT の結果が --More-- でページャ表示されますが、下記でページャ表示の有無を切り替えることができます。
\pset pager off \pset pager on
\H を実行すると SELECT の結果を HTMLモードで表示します。もう一度 \H を実行すると通常モードに戻ります。
\H SELECT * FROM users; <table border="1"> <tr> <th align="center">id</th> <th align="center">name</th> :
\set
\set USER_ID 'U001'
\echo :USER_ID
数値として参照したいときは :USER_ID、文字列として参照したいときは :'USER_ID' とします。
SELECT * FROM users WHERE id = :'USER_ID';
\i file
\h で SELECT などのコマンドのヘルプ、\? で \dt などのバックスラッシュコマンドのヘルプを表示することができます。
\h -- コマンド一覧 \h SELECT -- コマンドのヘルプ \? -- バックスラッシュコマンドの一覧 \? options -- psqlコマンドオプションの一覧 \? variables --
~/.psqlrc ファイルにコマンドを記述しておくと、ログイン時に自動実行してくれます。
$ cat > ~/.psqlrc <<EOF SET search_path TO mychema \pset pager off EOF
serial 型のカラムは INSERT する度に自動インクリメントされるシーケンス番号を保持することができます。
CREATE TABLE users (id serial PRIMARY KEY, name text); INSERT INTO users (name) VALUES ('Yamada'); INSERT INTO users (name) VALUES ('Tanaka'); SELECT * FROM users; id | name ----+-------- 1 | Yamada 2 | Tanaka (2 rows)
下記の様にしてシーケンス番号を確認したり変更したりできます。
SELECT currval(pg_catalog.pg_get_serial_sequence('users', 'id')); SELECT * FROM users_id_seq; SELECT SETVAL('users_id_seq', 100, true);
pg_dump でデータベースの内容を SQL にダンプすることができます。
$ pg_dump -h localhost -U postgres -d postgres > dump.sql