PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Porady i wskazówki dotyczące Postgresa

Czy na co dzień współpracujesz z Postgresem? Napisz kod aplikacji, który rozmawia z Postgresem? W takim razie sprawdź poniższe krótkie fragmenty kodu SQL, które pomogą Ci pracować szybciej!

Wstaw wiele wierszy w jednej instrukcji

Instrukcja INSERT może wstawić więcej niż jeden wiersz w jednej instrukcji:

INSERT INTO planets (name, gravity)
     VALUES ('earth',    9.8),
            ('mars',     3.7),
            ('jupiter', 23.1);

Przeczytaj więcej o możliwościach INSERT tutaj.

Wstaw wiersz i zwróć automatycznie przypisane wartości

Wartości generowane automatycznie za pomocą konstrukcji DEFAULT/serial/IDENTITY mogą być zwracane przez instrukcję INSERT przy użyciu klauzuli RETURNING. Z perspektywy kodu aplikacji taka INSERT jest wykonywana jak SELECT, która zwraca 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

Automatycznie wygenerowane klucze podstawowe UUID

Z różnych powodów identyfikatory UUID są czasami używane zamiast kluczy podstawowych. Tutaj ishow możesz użyć UUID zamiast numeru seryjnego lub TOŻSAMOŚCI:

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

Wstaw, jeśli nie istnieje, zaktualizuj inaczej

W Postgresie 9.5 i nowszych wersjach możesz upsert bezpośrednio za pomocą konstrukcji ON CONFLICT:

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;

Kopiuj wiersze z jednej tabeli do drugiej

Instrukcja INSERT ma postać, w której wartości mogą być dostarczane przez instrukcję SELECT. Użyj tego, aby skopiować wiersze z jednej tabeli do drugiej:

-- 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;

Jeśli chcesz zbiorczo ładować tabele, sprawdź także polecenie KOPIUJ, którego można użyć do wstawienia wierszy z pliku tekstowego lub CSV.

Usuń i zwróć usunięte informacje

Możesz użyć RETURNING klauzula zwracająca wartości z wierszy, które zostały usunięte za pomocą instrukcji bulk-delete:

-- return the list of customers whose licenses were deleted after expiry
DELETE FROM licenses
      WHERE now() > expiry_date
  RETURNING customer_name;

Przenieś wiersze z jednego stołu do drugiego

Możesz przenosić wiersze z jednej tabeli do drugiej w jednej instrukcji, używając poleceń CTE z 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;

Zaktualizuj wiersze i zwróć zaktualizowane wartości

Klauzula RETURNING może być również używana w aktualizacjach. Pamiętaj, że w ten sposób można zwrócić tylko nowe wartości zaktualizowanych kolumn.

-- grant random amounts of coins to eligible players
   UPDATE players
      SET coins = coins + (100 * random())::integer
    WHERE eligible
RETURNING id, coins;

Jeśli potrzebujesz oryginalnej wartości zaktualizowanych kolumn:jest to możliwe przez samosprzężenie, ale nie ma gwarancji niepodzielności. Spróbuj użyć SELECT .. FOR UPDATE zamiast tego.

Zaktualizuj kilka losowych wierszy i zwróć te zaktualizowane

Oto jak możesz wybrać kilka losowych wierszy z tabeli, zaktualizować je i zwrócić zaktualizowane, wszystko za jednym razem:

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;

Utwórz stół zupełnie jak inny stół

Użyj konstrukcji CREATE TABLE .. LIKE, aby utworzyć tabelę z takimi samymi kolumnami jak inna:

CREATE TABLE to_be_audited (LIKE purchases);

Domyślnie nie tworzy to podobnych indeksów, ograniczeń, wartości domyślnych itp. Aby to zrobić, zapytaj Postgresa wyraźnie:

CREATE TABLE to_be_audited (LIKE purchases INCLUDING ALL);

Zobacz pełną składnię tutaj.

Wyodrębnij losowy zestaw wierszy do innej tabeli

Od wersji Postgres 9.5 dostępna jest funkcja TABLESAMPLE, która pozwala wyodrębnić przykładowe wiersze z tabeli. Obecnie istnieją dwie metody próbkowania i bernoulli zwykle ten, który chcesz:

-- 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;

system Metoda próbkowania tabel jest szybsza, ale nie zwraca jednolitej dystrybucji. Więcej informacji znajdziesz w dokumentacji.

Tworzenie tabeli z zapytania wybierającego

Możesz użyć konstrukcji CREATE TABLE .. AS, aby utworzyć tabelę i wypełnić ją z zapytania SELECT, wszystko za jednym razem:

CREATE TABLE to_be_audited AS
      SELECT *
        FROM purchases
 TABLESAMPLE bernoulli(10)
       WHERE transaction_date = CURRENT_DATE;

Wynikowa tabela jest jak widok zmaterializowany bez skojarzonego z nim zapytania. Przeczytaj więcej o CREATE TABLE .. JAK tutaj.

Twórz niezalogowane tabele

Wylogowany tabele nie są obsługiwane przez rekordy WAL. Oznacza to, że aktualizacje i usunięcia takich tabel są szybsze, ale nie są odporne na awarie i nie można ich replikować.

CREATE UNLOGGED TABLE report_20200817 (LIKE report_v3);

Utwórz tabele tymczasowe

Tymczasowe tabele są niejawnie niezalogowanymi tabelami o krótszym okresie życia. Automatycznie ulegają samozniszczeniu na koniec sesji (domyślnie) lub na koniec transakcji.

Danych w tabelach tymczasowych nie można udostępniać między sesjami. Wiele sesji może tworzyć tymczasowe tabele o tej samej nazwie.

-- 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;

Dodaj komentarze

Komentarze można dodawać do dowolnego obiektu w bazie danych. Wiele narzędzi, w tym pg_dump, rozumie je. Przydatny komentarz może po prostu uniknąć mnóstwa kłopotliwego sprzątania!

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';

Zamki doradcze

Blokady doradcze mogą być używane do koordynowania działań między dwiema aplikacjami podłączonymi do tego samego Baza danych. Możesz użyć tej funkcji, aby na przykład zaimplementować globalny, rozproszony mutex dla określonej operacji. Przeczytaj o tym wszystko tutaj w dokumentacji.

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

Agregacja w tablice, tablice JSON lub ciągi znaków

Postgres udostępnia funkcje agregujące, które łączą wartości w GROUP tablice zabawki, tablice JSON lub ciągi znaków:

-- 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;

Agregacje z zamówieniem

Skoro już o tym mowa, oto jak ustawić kolejność wartości przekazywanych do funkcji agregującej w każdej grupie :

-- 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;

Tak, wewnątrz nawiasu wywołania funkcji znajduje się klauzula ORDER BY. Tak, składnia jest dziwna.

Tablica i rozpakowywanie

Użyj konstruktora ARRAY, aby przekonwertować zestaw wierszy, każdy z jedną kolumną, na tablicę. Sterownik bazy danych (np. JDBC) powinien być w stanie mapować tablice Postgres na tablice natywne i może być łatwiejszy w obsłudze.

-- convert rows (with 1 column each) into a 1-dimensional array
SELECT ARRAY(SELECT id FROM players WHERE lifetime_spend > 10000);

Funkcja unnest działa na odwrót – konwertuje każdy element tablicy na wiersz. Są najbardziej przydatne w łączeniu krzyżowym z listą wartości:

    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..)

Połącz instrukcje Select z Union

Możesz użyć konstrukcji UNION, aby połączyć wyniki z wielu podobnych SELECT:

SELECT name FROM weapons
UNION
SELECT name FROM tools
UNION
SELECT name FROM materials;

Użyj CTE do dalszego przetwarzania połączonego wyniku:

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;

Istnieją również konstrukcje INTERSECT i EXCEPT, podobnie jak UNION. Przeczytaj więcej o tych klauzulach w dokumentacji.

Szybkie poprawki w Select:case, coalesce i nullif

CASE, COALESCE i NULLIF do małych szybkich „poprawek” dla WYBRANYCH danych.CASE jest jak przełącznik w językach podobnych do 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 można użyć do zastąpienia określonej wartości zamiast 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 działa w drugą stronę, pozwalając na użycie NULL zamiast określonej wartości:

-- use NULL instead of '0.0.0.0'
SELECT nodename, NULLIF(ipv4, '0.0.0.0') FROM nodes;

Generuj losowe i sekwencyjne dane testowe

Różne metody generowania losowych danych:

-- 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);

Użyj bernoulli próbkowanie tabeli, aby wybrać losową liczbę wierszy z tabeli:

-- select 15% of rows from the table, chosen randomly  
     SELECT *
       FROM purchases
TABLESAMPLE bernoulli(15)

Użyj generate_series do generowania kolejnych wartości liczb całkowitych, dat i innych inkrementalnych wbudowanych typów:

-- 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');

Pobierz przybliżoną liczbę wierszy

Straszna wydajność COUNT(*) jest prawdopodobnie najbrzydszym produktem ubocznym architektury Postgres. Jeśli potrzebujesz tylko przybliżonej liczby wierszy dla ogromnej tabeli, możesz uniknąć pełnej LICZBY, wysyłając zapytanie do kolektora statystyk:

SELECT relname, n_live_tup FROM pg_stat_user_tables;

Wynik jest dokładny po przeprowadzeniu ANALIZY i będzie stopniowo niepoprawny w miarę modyfikowania wierszy. Nie używaj tego, jeśli chcesz uzyskać dokładne obliczenia.

Typ interwału

przedział type może być nie tylko używany jako typ danych kolumny, ale może być dodawany i odejmowany od data i sygnatura czasowa wartości:

-- 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;

Wyłącz weryfikację ograniczeń dla wstawiania zbiorczego

-- 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;

Zrzuć tabelę lub zapytanie do pliku 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 );

Użyj większej liczby natywnych typów danych w swoim projekcie schematu

Postgres zawiera wiele wbudowanych typów danych. Reprezentowanie danych, których potrzebuje Twoja aplikacja za pomocą jednego z tych typów, może zaoszczędzić dużo kodu aplikacji, przyspieszyć programowanie i skutkować mniejszą liczbą błędów.

Na przykład, jeśli reprezentujesz lokalizację osoby za pomocą typu danychpoint i region zainteresowania jako polygon , możesz sprawdzić, czy osoba znajduje się w regionie, po prostu za pomocą:

-- the @> operator checks if the region of interest (a "polygon") contains
-- the person's location (a "point")
SELECT roi @> person_location FROM live_tracking;

Oto kilka interesujących typów danych Postgres i linki, w których można znaleźć więcej informacji na ich temat:

  • Typy wyliczeniowe podobne do C
  • Typy geometryczne – punkt, prostokąt, odcinek, linia, ścieżka, wielokąt, okrąg
  • Adresy IPv4, IPv6 i MAC
  • Typy zakresów – zakresy liczb całkowitych, dat i znaczników czasu
  • Tablice, które mogą zawierać wartości dowolnego typu
  • UUID – jeśli potrzebujesz użyć identyfikatorów UUID lub po prostu musisz pracować ze 129-bajtowymi losowymi liczbami całkowitymi, rozważ użycie uuid typ i uuid-oscp rozszerzenie do przechowywania, generowania i formatowania UUID
  • Przedziały dat i czasu przy użyciu typu INTERWAŁ
  • i oczywiście zawsze popularne JSON i JSONB

Dołączone rozszerzenia

Większość instalacji Postgresa zawiera kilka standardowych „rozszerzeń”. Rozszerzenia to komponenty, które można zainstalować (i całkowicie odinstalować), które zapewniają funkcje, które nie są zawarte w rdzeniu. Mogą być instalowane na podstawie bazy danych.

Niektóre z nich są całkiem przydatne i warto poświęcić trochę czasu na ich poznanie:

  • pg_stat_statements – statystyki dotyczące wykonania każdego zapytania SQL
  • auto_explain – rejestruj plan wykonywania zapytań (powolnych)
  • postgres_fdw,dblink andfile_fdw – sposoby dostępu do innych źródeł danych (takich jak zdalne serwery Postgres, serwery MySQL, pliki w systemie plików serwera), jak zwykłe tabele
  • citext – typ danych „tekst bez rozróżniania wielkości liter”, bardziej wydajny niż funkcja lower() w całym miejscu
  • hstore – typ klucz-wartość danych
  • pgcrypto – funkcje haszujące SHA, szyfrowanie

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Zapytanie, które ignoruje spacje

  2. python pip zainstalować błąd instalacji psycopg2

  3. SequelizeConnectionError:samopodpisany certyfikat

  4. Nie można po prostu użyć nazwy tabeli PostgreSQL (relacja nie istnieje)

  5. Jak używać (zainstalować) dblink w PostgreSQL?