毎日Postgresを使用していますか? Postgresと通信するアプリケーションコードを記述しますか?次に、作業を高速化するのに役立つ、一口サイズのSQLスニペットを確認してください。
INSERTステートメントは、1つのステートメントに複数の行を挿入できます。
INSERT INTO planets (name, gravity)
VALUES ('earth', 9.8),
('mars', 3.7),
('jupiter', 23.1);
INSERTでできることについて詳しくはこちらをご覧ください。
行を挿入し、自動的に割り当てられた値を返します
DEFAULT / serial / IDENTITY構文で自動生成された値は、RETURNING句を使用してINSERTステートメントで返すことができます。アプリケーションコードの観点からは、このようなINSERTはレコードセットを返すSELECTのように実行されます。
-- table with 2 column values auto-generated on INSERT
CREATE TABLE items (
slno serial PRIMARY KEY,
name text NOT NULL,
created_at timestamptz DEFAULT now()
);
INSERT INTO items (name)
VALUES ('wooden axe'),
('loom'),
('eye of ender')
RETURNING name, slno, created_at;
-- returns:
-- name | slno | created_at
-- --------------+------+-------------------------------
-- wooden axe | 1 | 2020-08-17 05:35:45.962725+00
-- loom | 2 | 2020-08-17 05:35:45.962725+00
-- eye of ender | 3 | 2020-08-17 05:35:45.962725+00
さまざまな理由で、主キーの代わりにUUIDが使用されることがあります。シリアルまたはIDENTITYの代わりにUUIDを使用する方法は次のとおりです。
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE items (
id uuid DEFAULT uuid_generate_v4(),
name text NOT NULL
);
INSERT INTO items (name)
VALUES ('wooden axe'),
('loom'),
('eye of ender')
RETURNING id, name;
-- returns:
-- id | name
-- --------------------------------------+--------------
-- 1cfaae8c-61ff-4e82-a656-99263b7dd0ae | wooden axe
-- be043a89-a51b-4d8b-8378-699847113d46 | loom
-- 927d52eb-c175-4a97-a0b2-7b7e81d9bc8e | eye of ender
存在しない場合は挿入、そうでない場合は更新
Postgres 9.5以降では、アップサートできます。 ON CONFLICTconstructを直接使用する:
CREATE TABLE parameters (
key TEXT PRIMARY KEY,
value TEXT
);
-- when "key" causes a constraint violation, update the "value"
INSERT INTO parameters (key, value)
VALUES ('port', '5432')
ON CONFLICT (key) DO
UPDATE SET value=EXCLUDED.value;
INSERTステートメントには、SELECTステートメントによって値を指定できる形式があります。これを使用して、あるテーブルから別のテーブルに行をコピーします。
-- copy between tables with similar columns
INSERT INTO pending_quests
SELECT * FROM quests
WHERE progress < 100;
-- supply some values from another table, some directly
INSERT INTO archived_quests
SELECT now() AS archival_date, *
FROM quests
WHERE completed;
テーブルの一括読み込みを検討している場合は、テキストまたはCSVファイルから行を挿入するために使用できるCOPYコマンドも確認してください。
RETURNING
を使用できます 一括削除ステートメントを使用して削除された行から値を返す句:
-- return the list of customers whose licenses were deleted after expiry
DELETE FROM licenses
WHERE now() > expiry_date
RETURNING customer_name;
DELETE .. RETURNING でCTEを使用すると、1つのステートメントで1つのテーブルから別のテーブルに行を移動できます。 :
-- move yet-to-start todo items from 2020 to 2021
WITH ah_well AS (
DELETE FROM todos_2020
WHERE NOT started
RETURNING *
)
INSERT INTO todos_2021
SELECT * FROM ah_well;
RETURNING句は、UPDATEでも使用できます。この方法で返すことができるのは、更新された列の新しい値のみであることに注意してください。
-- grant random amounts of coins to eligible players
UPDATE players
SET coins = coins + (100 * random())::integer
WHERE eligible
RETURNING id, coins;
更新された列の元の値が必要な場合:自己結合によって可能ですが、アトミック性の保証はありません。 SELECT .. FOR
UPDATE
を使用してみてください 代わりに。
いくつかのランダムな行を更新し、更新された行を返します
テーブルからランダムな行をいくつか選択し、それらを更新して、更新された行を一度に返す方法は次のとおりです。
WITH lucky_few AS (
SELECT id
FROM players
ORDER BY random()
LIMIT 5
)
UPDATE players
SET bonus = bonus + 100
WHERE id IN (SELECT id FROM lucky_few)
RETURNING id;
CREATE TABLE .. LIKE構文を使用して、別の列と同じ列を持つテーブルを作成します。
CREATE TABLE to_be_audited (LIKE purchases);
デフォルトでは、これは同様のインデックス、制約、デフォルトなどを作成しません。それを行うには、Postgresに明示的に尋ねてください:
CREATE TABLE to_be_audited (LIKE purchases INCLUDING ALL);
ここで完全な構文を参照してください。
Postgres 9.5以降、TABLESAMPLE機能を使用してテーブルから行のサンプルを抽出できます。現在、2つのサンプリング方法があり、 bernoulli 通常、必要なもの:
-- copy 10% of today's purchases into another table
INSERT INTO to_be_audited
SELECT *
FROM purchases
TABLESAMPLE bernoulli(10)
WHERE transaction_date = CURRENT_DATE;
システム テーブルサンプリングメソッドは高速ですが、均一な分布を返しません。詳細については、ドキュメントを参照してください。
CREATE TABLE .. ASコンストラクトを使用して、SELECTクエリからテーブルを作成し、データを入力することができます。
CREATE TABLE to_be_audited AS
SELECT *
FROM purchases
TABLESAMPLE bernoulli(10)
WHERE transaction_date = CURRENT_DATE;
結果のテーブルは、クエリが関連付けられていないマテリアライズドビューのようなものです。 CREATE TABLE..ASの詳細についてはこちらをご覧ください。
ログなし テーブルはWALレコードによってサポートされていません。つまり、このようなテーブルの更新と削除は高速ですが、クラッシュ耐性がなく、複製できません。
CREATE UNLOGGED TABLE report_20200817 (LIKE report_v3);
一時的 テーブルは暗黙的にログに記録されないテーブルであり、存続期間は短くなります。セッションの終了時(デフォルト)またはトランザクションの終了時に自動的に自己破壊します。
一時テーブル内のデータは、セッション間で共有できません。複数のセッションで同じ名前の一時テーブルを作成できます。
-- temp table for duration of the session
CREATE TEMPORARY TABLE scratch_20200817_run_12 (LIKE report_v3);
-- temp table that will self-destruct after current transaction
CREATE TEMPORARY TABLE scratch_20200817_run_12
(LIKE report_v3)
ON COMMIT DROP;
-- temp table that will TRUNCATE itself after current transaction
CREATE TEMPORARY TABLE scratch_20200817_run_12
(LIKE report_v3)
ON COMMIT DELETE ROWS;
コメントは、データベース内の任意のオブジェクトに追加できます。 pg_dumpを含む多くのツールは、これらを理解しています。有用なコメントは、クリーンアップ中の大量の問題を回避するだけかもしれません!
COMMENT ON INDEX idx_report_last_updated
IS 'needed for the nightly report app running in dc-03';
COMMENT ON TRIGGER tgr_fix_column_foo
IS 'mitigates the effect of bug #4857';
アドバイザリロックを使用して、同じに接続された2つのアプリ間のアクションを調整できます。 データベース。この機能を使用して、たとえば、特定の操作に対してグローバルな分散ミューテックスを実装できます。ここのドキュメントでそれについてすべて読んでください。
-- client 1: acquire a lock
SELECT pg_advisory_lock(130);
-- ... do work ...
SELECT pg_advisory_unlock(130);
-- client 2: tries to do the same thing, but mutually exclusive
-- with client 1
SELECT pg_advisory_lock(130); -- blocks if anyone else has held lock with id 130
-- can also do it without blocking:
SELECT pg_try_advisory_lock(130);
-- returns false if lock is being held by another client
-- otherwise acquires the lock then returns true
配列、JSON配列、または文字列に集約
Postgresは、 GROUPの値を連結する集計関数を提供します toyield配列、JSON配列、または文字列:
-- get names of each guild, with an array of ids of players that
-- belong to that guild
SELECT guilds.name AS guild_name, array_agg(players.id) AS players
FROM guilds
JOIN players ON players.guild_id = guilds.id
GROUP BY guilds.id;
-- same but the player list is a CSV string
SELECT guilds.name, string_agg(players.id, ',') -- ...
-- same but the player list is a JSONB array
SELECT guilds.name, jsonb_agg(players.id) -- ...
-- same but returns a nice JSONB object like so:
-- { guild1: [ playerid1, playerid2, .. ], .. }
SELECT jsonb_object_agg(guild_name, players) FROM (
SELECT guilds.name AS guild_name, array_agg(players.id) AS players
FROM guilds
JOIN players ON players.guild_id = guilds.id
GROUP BY guilds.id
) AS q;
このトピックについて説明している間、各グループ内で集計関数に渡される値の順序を設定する方法は次のとおりです。 :
-- each state with a list of counties sorted alphabetically
SELECT states.name, string_agg(counties.name, ',' ORDER BY counties.name)
FROM states JOIN counties
JOIN states.name = counties.state_name
GROUP BY states.name;
はい、関数呼び出しparanthesis内に末尾のORDER BY句があります。はい、構文がおかしいです。
ARRAYコンストラクターを使用して、それぞれが1つの列を持つ行のセットを配列に変換します。データベースドライバー(JDBCなど)は、Postgresアレイをネイティブアレイにマップできる必要があり、操作が簡単な場合があります。
-- convert rows (with 1 column each) into a 1-dimensional array
SELECT ARRAY(SELECT id FROM players WHERE lifetime_spend > 10000);
unnest関数はその逆を行い、配列内の各項目を行に変換します。これらは、値のリストとの相互結合に最も役立ちます。
SELECT materials.name || ' ' || weapons.name
FROM weapons
CROSS JOIN UNNEST('{"wood","gold","stone","iron","diamond"}'::text[])
AS materials(name);
-- returns:
-- ?column?
-- -----------------
-- wood sword
-- wood axe
-- wood pickaxe
-- wood shovel
-- gold sword
-- gold axe
-- (..snip..)
UNION構文を使用して、複数の類似したSELECTからの結果を組み合わせることができます:
SELECT name FROM weapons
UNION
SELECT name FROM tools
UNION
SELECT name FROM materials;
CTEを使用して、結合された結果をさらに処理します。
WITH fight_equipment AS (
SELECT name, damage FROM weapons
UNION
SELECT name, damage FROM tools
)
SELECT name, damage
FROM fight_equipment
ORDER BY damage DESC
LIMIT 5;
UNIONと同じように、INTERSECTおよびEXCEPTコンストラクトもあります。ドキュメントでこれらの条項の詳細をご覧ください。
Selectのクイックフィックス:case、colesce、nullif
CASE、COALESCE、およびNULLIFを使用して、選択したデータをすばやく「修正」します。CASEはスイッチのようなものです。 Cのような言語の場合:
SELECT id,
CASE WHEN name='typ0' THEN 'typo' ELSE name END
FROM items;
SELECT CASE WHEN rating='G' THEN 'General Audiences'
WHEN rating='PG' THEN 'Parental Guidance'
ELSE 'Other'
END
FROM movies;
COALESCEを使用して、NULLの代わりに特定の値を置き換えることができます。
-- use an empty string if ip is not available
SELECT nodename, COALESCE(ip, '') FROM nodes;
-- try to use the first available, else use '?'
SELECT nodename, COALESCE(ipv4, ipv6, hostname, '?') FROM nodes;
NULLIFは逆の方法で機能し、特定の値の代わりにNULLを使用できるようにします。
-- use NULL instead of '0.0.0.0'
SELECT nodename, NULLIF(ipv4, '0.0.0.0') FROM nodes;
ランダムデータを生成するさまざまな方法:
-- 100 random dice rolls
SELECT 1+(5 * random())::int FROM generate_series(1, 100);
-- 100 random text strings (each 32 chars long)
SELECT md5(random()::text) FROM generate_series(1, 100);
-- 100 random text strings (each 36 chars long)
SELECT uuid_generate_v4()::text FROM generate_series(1, 100);
-- 100 random small text strings of varying lengths
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
SELECT gen_random_bytes(1+(9*random())::int)::text
FROM generate_series(1, 100);
-- 100 random dates in 2019
SELECT DATE(
DATE '2019-01-01' + ((random()*365)::int || ' days')::interval
)
FROM generate_series(1, 100);
-- 100 random 2-column data: 1st column integer and 2nd column string
WITH a AS (
SELECT ARRAY(SELECT random() FROM generate_series(1,100))
),
b AS (
SELECT ARRAY(SELECT md5(random()::text) FROM generate_series(1,100))
)
SELECT unnest(i), unnest(j)
FROM a a(i), b b(j);
-- a daily count for 2020, generally increasing over time
SELECT i, ( (5+random()) * (row_number() over()) )::int
FROM generate_series(DATE '2020-01-01', DATE '2020-12-31', INTERVAL '1 day')
AS s(i);
bernoulliを使用する テーブルからランダムな数の行を選択するためのテーブルサンプリング:
-- select 15% of rows from the table, chosen randomly
SELECT *
FROM purchases
TABLESAMPLE bernoulli(15)
generate_series
を使用します 整数、日付、およびその他の増分可能な組み込み型の順次値を生成するには:
-- generate integers from 1 to 100
SELECT generate_series(1, 100);
-- call the generated values table as "s" with a column "i", to use in
-- CTEs and JOINs
SELECT i FROM generate_series(1, 100) AS s(i);
-- generate multiples of 3 in different ways
SELECT 3*i FROM generate_series(1, 100) AS s(i);
SELECT generate_series(1, 100, 3);
-- works with dates too: here are all the Mondays in 2020:
SELECT generate_series(DATE '2020-01-06', DATE '2020-12-31', INTERVAL '1 week');
COUNT(*)
の恐ろしいパフォーマンス おそらくPostgresのアーキテクチャの最も醜い副産物です。巨大なテーブルのおおよその行数が必要な場合は、統計コレクターにクエリを実行することで、完全なCOUNTを回避できます。
SELECT relname, n_live_tup FROM pg_stat_user_tables;
ANALYZE後の結果は正確であり、行が変更されるにつれて徐々に不正確になります。正確なカウントが必要な場合は、これを使用しないでください。
間隔 typeは、列のデータ型として使用できるだけでなく、 dateに加算および減算することもできます。 およびタイムスタンプ 値:
-- get licenses that expire within the next 7 days
SELECT id
FROM licenses
WHERE expiry_date BETWEEN now() - INTERVAL '7 days' AND now();
-- extend expiry date
UPDATE licenses
SET expiry_date = expiry_date + INTERVAL '1 year'
WHERE id = 42;
-- add a constraint, set as "not valid"
ALTER TABLE players
ADD CONSTRAINT fk__players_guilds
FOREIGN KEY (guild_id)
REFERENCES guilds(id)
NOT VALID;
-- insert lots of rows into the table
COPY players FROM '/data/players.csv' (FORMAT CSV);
-- now validate the entire table
ALTER TABLE players
VALIDATE CONSTRAINT fk__players_guilds;
-- dump the contents of a table to a CSV format file on the server
COPY players TO '/tmp/players.csv' (FORMAT CSV);
-- "header" adds a heading with column names
COPY players TO '/tmp/players.csv' (FORMAT CSV, HEADER);
-- use the psql command to save to your local machine
\copy players TO '~/players.csv' (FORMAT CSV);
-- can use a query instead of a table name
\copy ( SELECT id, name, score FROM players )
TO '~/players.csv'
( FORMAT CSV );
Postgresには多くの組み込みデータ型が付属しています。これらのタイプのいずれかを使用してアプリケーションに必要なデータを表すと、多くのアプリケーションコードを節約し、開発を高速化し、エラーを減らすことができます。
たとえば、データ型point
を使用して個人の場所を表す場合 polygon
としての関心領域 、次のコマンドで、その地域の人物がいるかどうかを確認できます。
-- the @> operator checks if the region of interest (a "polygon") contains
-- the person's location (a "point")
SELECT roi @> person_location FROM live_tracking;
ここにいくつかの興味深いPostgresデータ型とそれらに関する詳細情報を見つけることができる場所へのリンクがあります:
- Cのような列挙型
- ジオメトリタイプ–ポイント、ボックス、ラインセグメント、ライン、パス、ポリゴン、円
- IPv4、IPv6、およびMACアドレス
- 範囲タイプ–整数、日付、タイムスタンプの範囲
- 任意のタイプの値を含むことができる配列
- UUID – UUIDを使用する必要がある場合、または129バイトのランダムな整数を処理する必要がある場合は、
uuid
の使用を検討してください。 タイプとuuid-oscp
UUIDの保存、生成、フォーマットのための拡張機能 - INTERVALタイプを使用した日付と時刻の間隔
- そしてもちろん、人気の高いJSONとJSONB
ほとんどのPostgresインストールには、多数の標準的な「拡張機能」が含まれています。拡張機能は、コアに含まれていない機能を提供するインストール可能な(そして完全にアンインストール可能な)コンポーネントです。データベースごとにインストールできます。
これらのいくつかは非常に便利であり、それらを知るために時間を費やす価値があります:
- pg_stat_statements –各SQLクエリの実行に関する統計
- auto_explain –(遅い)クエリのクエリ実行プランをログに記録する
- postgres_fdw、dblink、file_fdw –通常のテーブルのような他のデータソース(リモートのPostgresサーバー、MySQLサーバー、サーバーのファイルシステム上のファイルなど)にアクセスする方法
- citext –「大文字と小文字を区別しないテキスト」データ型。あらゆる場所でlower()-ingよりも効率的です
- hstore –キーと値のデータタイプ
- pgcrypto –SHAハッシュ関数、暗号化