Apakah Anda bekerja dengan Postgres setiap hari? Tulis kode aplikasi yang berbicara dengan Postgres? Kemudian lihat cuplikan SQL berukuran kecil di bawah ini yang dapat membantu Anda bekerja lebih cepat!
Sisipkan Beberapa Baris Dalam Satu Pernyataan
Pernyataan INSERT dapat menyisipkan lebih dari satu baris dalam satu pernyataan:
INSERT INTO planets (name, gravity)
VALUES ('earth', 9.8),
('mars', 3.7),
('jupiter', 23.1);
Baca lebih lanjut tentang apa yang dapat dilakukan INSERT di sini.
Sisipkan Baris dan Kembalikan Nilai yang Ditetapkan Secara Otomatis
Nilai yang dihasilkan secara otomatis dengan konstruksi DEFAULT/serial/IDENTITY dapat dikembalikan oleh pernyataan INSERT menggunakan klausa RETURNING. Dari perspektif kode aplikasi, INSERT seperti itu dijalankan seperti SELECT yang mengembalikan arecordset.
-- 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
Kunci Utama UUID yang dibuat secara otomatis
UUID terkadang digunakan sebagai pengganti kunci utama karena berbagai alasan. Inilah cara Anda dapat menggunakan UUID alih-alih serial atau IDENTITAS:
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
Sisipkan Jika Tidak Ada, Perbarui Jika Tidak
Di Postgres 9.5 dan yang lebih baru, Anda dapat memasang langsung menggunakan 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;
Menyalin Baris Dari Satu Tabel Ke Tabel Lain
Pernyataan INSERT memiliki bentuk di mana nilai dapat diberikan oleh pernyataan SELECT. Gunakan ini untuk menyalin baris dari satu tabel ke tabel lain:
-- 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;
Jika Anda ingin memuat tabel secara massal, lihat juga perintah COPY, yang dapat digunakan untuk menyisipkan baris dari teks atau file CSV.
Hapus dan Kembalikan Informasi yang Dihapus
Anda dapat menggunakan RETURNING
klausa untuk mengembalikan nilai dari baris yang dihapus menggunakan pernyataan penghapusan massal:
-- return the list of customers whose licenses were deleted after expiry
DELETE FROM licenses
WHERE now() > expiry_date
RETURNING customer_name;
Pindahkan Baris Dari Satu Tabel Ke Tabel Lain
Anda dapat memindahkan baris dari satu tabel ke tabel lainnya dalam satu pernyataan, dengan menggunakan CTE dengan DELETE .. RETURNING :
-- 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;
Perbarui Baris dan Kembalikan Nilai yang Diperbarui
Klausa RETURNING dapat digunakan dalam UPDATE juga. Perhatikan bahwa hanya nilai baru dari kolom yang diperbarui yang dapat dikembalikan dengan cara ini.
-- grant random amounts of coins to eligible players
UPDATE players
SET coins = coins + (100 * random())::integer
WHERE eligible
RETURNING id, coins;
Jika Anda memerlukan nilai asli dari kolom yang diperbarui:dimungkinkan melalui self-join, tetapi tidak ada jaminan atomisitas. Coba gunakan SELECT .. FOR
UPDATE
sebagai gantinya.
Perbarui Beberapa Baris Acak dan Kembalikan Yang Diperbarui
Inilah cara Anda dapat memilih beberapa baris acak dari tabel, memperbaruinya, dan mengembalikan yang diperbarui, semuanya sekaligus:
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;
Buat Tabel Seperti Tabel Lain
Gunakan konstruksi CREATE TABLE .. LIKE untuk membuat tabel dengan kolom yang sama seperti yang lain:
CREATE TABLE to_be_audited (LIKE purchases);
Secara default ini tidak membuat indeks, batasan, default, dll yang serupa. Untuk melakukannya, tanyakan Postgres secara eksplisit:
CREATE TABLE to_be_audited (LIKE purchases INCLUDING ALL);
Lihat sintaks lengkapnya di sini.
Ekstrak Set Baris Acak Ke Tabel Lain
Sejak Postgres 9.5, fitur TABLESAMPLE tersedia untuk mengekstrak contoh baris dari tabel. Ada dua metode pengambilan sampel saat ini, dan bernoulli biasanya yang Anda inginkan:
-- 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;
sistem metode tablesampling lebih cepat, tetapi tidak mengembalikan distribusi yang seragam. Lihat dokumen untuk info lebih lanjut.
Buat Tabel Dari Kueri Pilihan
Anda dapat menggunakan konstruksi CREATE TABLE .. AS untuk membuat tabel dan mengisinya dari kueri SELECT, semuanya sekaligus:
CREATE TABLE to_be_audited AS
SELECT *
FROM purchases
TABLESAMPLE bernoulli(10)
WHERE transaction_date = CURRENT_DATE;
Tabel yang dihasilkan seperti tampilan terwujud tanpa kueri yang terkait dengannya. Baca lebih lanjut tentang CREATE TABLE .. AS di sini.
Buat Tabel Tanpa Catatan
Tidak Tercatat tabel tidak didukung oleh catatan WAL. Ini berarti bahwa pembaruan dan penghapusan ke tabel tersebut lebih cepat, tetapi tidak toleran terhadap kerusakan dan tidak dapat direplikasi.
CREATE UNLOGGED TABLE report_20200817 (LIKE report_v3);
Buat Tabel Sementara
Sementara tabel secara implisit adalah tabel yang tidak dicatat, dengan masa pakai yang lebih pendek. Mereka secara otomatis merusak diri sendiri di akhir sesi (default), atau di akhir transaksi.
Data dalam tabel sementara tidak dapat dibagikan di seluruh sesi. Beberapa sesi dapat membuat tabel sementara dengan nama yang sama.
-- 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;
Tambahkan Komentar
Komentar dapat ditambahkan ke objek apa pun dalam database. Banyak alat, termasukpg_dump, memahami ini. Komentar yang berguna mungkin akan menghindari banyak masalah selama pembersihan!
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';
Kunci Saran
Kunci penasihat dapat digunakan untuk mengoordinasikan tindakan antara dua aplikasi yang terhubung ke sama basis data. Anda dapat menggunakan fitur ini untuk mengimplementasikan mutex global terdistribusi untuk operasi tertentu, misalnya. Baca semuanya di sini di thedocs.
-- 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
Agregat Menjadi Array, JSON Array, atau String
Postgres menyediakan fungsi agregat yang menggabungkan nilai dalam GROUP array toyield, array atau string 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;
Agregat Dengan Pesanan
Saat kita membahas topik ini, berikut cara menyetel urutan nilai yang diteruskan ke fungsi agregat, dalam setiap grup :
-- 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;
Ya, ada klausa ORDER BY tambahan di dalam fungsi pemanggilan paranthesis.Ya, sintaksnya aneh.
Array dan Unnest
Gunakan konstruktor ARRAY untuk mengonversi sekumpulan baris, masing-masing dengan satu kolom, menjadi array. Driver database (seperti JDBC) harus dapat memetakan array Postgres ke dalam array asli dan mungkin lebih mudah untuk digunakan.
-- convert rows (with 1 column each) into a 1-dimensional array
SELECT ARRAY(SELECT id FROM players WHERE lifetime_spend > 10000);
Fungsi unnest melakukan kebalikannya – ia mengubah setiap item dalam array menjadi panah. Mereka paling berguna dalam penggabungan silang dengan daftar nilai:
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..)
Gabungkan Pernyataan Terpilih Dengan Union
Anda dapat menggunakan konstruksi UNION untuk menggabungkan hasil dari beberapa SELECT serupa:
SELECT name FROM weapons
UNION
SELECT name FROM tools
UNION
SELECT name FROM materials;
Gunakan CTE untuk memproses lebih lanjut hasil gabungan:
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;
Ada juga konstruksi INTERSECT dan KECUALI, dalam nada yang sama dengan UNION. Baca lebih lanjut tentang klausa ini di thedocs.
Perbaikan Cepat dalam Pilihan:huruf besar, gabungkan, dan nullif
CASE, COALESCE, dan NULLIF untuk membuat "perbaikan" kecil yang cepat untuk data SELECTed.CASE seperti switch dalam bahasa mirip-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 dapat digunakan untuk menggantikan nilai tertentu, bukan 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 bekerja sebaliknya, memungkinkan Anda menggunakan NULL alih-alih nilai tertentu:
-- use NULL instead of '0.0.0.0'
SELECT nodename, NULLIF(ipv4, '0.0.0.0') FROM nodes;
Hasilkan Data Uji Acak dan Berurutan
Berbagai metode menghasilkan data acak:
-- 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);
Gunakan bernoulli pengambilan sampel tabel untuk memilih jumlah baris acak dari tabel:
-- select 15% of rows from the table, chosen randomly
SELECT *
FROM purchases
TABLESAMPLE bernoulli(15)
Gunakan generate_series
untuk menghasilkan nilai berurutan bilangan bulat, tanggal, dan tipe bawaan tambahan lainnya:
-- 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');
Dapatkan Perkiraan Jumlah Baris
Performa mengerikan COUNT(*)
mungkin merupakan produk sampingan paling jelek dari arsitektur Postgres. Jika Anda hanya memerlukan perkiraan jumlah baris untuk tabel besar, Anda dapat menghindari COUNT penuh dengan menanyakan kolektor statistik:
SELECT relname, n_live_tup FROM pg_stat_user_tables;
Hasilnya akurat setelah ANALISIS, dan akan semakin salah saat baris dimodifikasi. Jangan gunakan ini jika Anda ingin penghitungan yang akurat.
Jenis Interval
Interval type tidak hanya dapat digunakan sebagai tipe data kolom, tetapi dapat ditambahkan dan dikurangi dari tanggal dan stempel waktu nilai:
-- 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;
Matikan Validasi Batasan Untuk Sisipan Massal
-- 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;
Buang Tabel atau Kueri ke File CSV
-- 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 );
Gunakan Lebih Banyak Jenis Data Asli Dalam Desain Skema Anda
Postgres hadir dengan banyak tipe data bawaan. Mewakili data yang dibutuhkan aplikasi Anda menggunakan salah satu jenis ini dapat menghemat banyak kode aplikasi, membuat pengembangan Anda lebih cepat, dan menghasilkan lebih sedikit kesalahan.
Misalnya, jika Anda mewakili lokasi seseorang menggunakan tipe datapoint
dan wilayah yang diinginkan sebagai polygon
, Anda dapat memeriksa apakah orang tersebut berada di wilayah tersebut hanya dengan:
-- the @> operator checks if the region of interest (a "polygon") contains
-- the person's location (a "point")
SELECT roi @> person_location FROM live_tracking;
Berikut adalah beberapa tipe data Postgres yang menarik dan tautan ke tempat Anda dapat menemukan informasi lebih lanjut tentangnya:
- Jenis enum seperti C
- Jenis geometris – titik, kotak, ruas garis, garis, jalur, poligon, lingkaran
- Alamat IPv4, IPv6 dan MAC
- Jenis rentang – rentang bilangan bulat, tanggal, dan stempel waktu
- Array yang dapat berisi nilai jenis apa pun
- UUID – jika Anda perlu menggunakan UUID, atau hanya perlu bekerja dengan bilangan bulat acak 129-byte, pertimbangkan untuk menggunakan
uuid
ketik danuuid-oscp
ekstensi untuk menyimpan, membuat, dan memformat UUID - Interval tanggal dan waktu menggunakan jenis INTERVAL
- dan tentu saja JSON dan JSONB yang selalu populer
Ekstensi yang Dibundel
Sebagian besar pemasangan Postgres menyertakan banyak "ekstensi" standar. Ekstensi adalah komponen yang dapat dipasang (dan tidak dapat dicopot) yang menyediakan fungsionalitas yang tidak termasuk dalam inti. Mereka dapat diinstal per basis data.
Beberapa di antaranya cukup berguna, dan ada baiknya meluangkan waktu untuk mengenal mereka:
- pg_stat_statements – statistik mengenai eksekusi setiap query SQL
- auto_explain –log rencana eksekusi kueri kueri (lambat)
- postgres_fdw,dblink andfile_fdw – cara untuk mengakses sumber data lain (seperti server Postgres jarak jauh, server MySQL, file pada sistem file server) seperti tabel biasa
- citext – tipe data “teks peka huruf besar/kecil”, lebih efisien daripada yang lebih rendah() di semua tempat
- hstore – tipe data nilai-kunci
- pgcrypto –Fungsi hashing SHA, enkripsi