PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Tips Dan Trik Postgres

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 dan uuid-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

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL - Buat tampilan dari beberapa tabel

  2. Mengimpor shapefile di postgresql di linux menggunakan pgadmin 4

  3. Pencarian teks lengkap sejak PostgreSQL 8.3

  4. Cara menjatuhkan beberapa tabel di PostgreSQL menggunakan wildcard

  5. Bagaimana cara menggunakan pg_stat_activity?