sql >> Database teknologi >  >> RDS >> PostgreSQL

Postgres tips og tricks

Arbejder du med Postgres til daglig? Skrive ansøgningskode, der taler til Postgres? Så tjek de små SQL-uddrag nedenfor, der kan hjælpe dig med at arbejde hurtigere!

Indsæt flere rækker i ét udsagn

INSERT-sætningen kan indsætte mere end én række i en enkelt sætning:

INDSÆT I planeter (navn, tyngdekraft) VÆRDIER ('jord', 9.8), ('mars', 3.7), ('jupiter', 23.1); 

Læs mere om, hvad INSERT kan her.

Indsæt en række og returner automatisk tildelte værdier

Værdier, der er automatisk genereret med DEFAULT/serial/IDENTITY-konstruktioner, kan returneres af INSERT-sætningen ved hjælp af RETURNING-udtrykket. Fra applikationskodeperspektivet udføres en sådan INSERT som en SELECT, der returnerer arecordset.

-- tabel med 2 kolonneværdier genereret automatisk på INSERTCREATE TABLE-elementer (slno seriel PRIMÆR NØGLE, navnetekst IKKE NULL, oprettet_på timestamptz DEFAULT nu());INSERT INTO items (navn) VALUES ('wooden axe) '), ('loom'), ('eye of end') RETURNERENDE navn, slno, oprettet_at;-- returnerer:-- navn | slno | oprettet_ved-- --------------+------+------------------------- -------- træøkse | 1 | 2020-08-17 05:35:45.962725+00-- væv | 2 | 2020-08-17 05:35:45.962725+00-- eye of ender | 3 | 2020-08-17 05:35:45.962725+00 

Autogenererede UUID-primære nøgler

UUID'er bruges nogle gange i stedet for primære nøgler af forskellige årsager. Her ishow kan du bruge en UUID i stedet for en seriel eller IDENTITY:

OPRET UDVIDELSE HVIS IKKE FINDER "uuid-ossp";CREATE TABLE items ( id uuid DEFAULT uuid_generate_v4(), navnetekst IKKE NULL);INSERT INTO items (name) VALUES ('wooden axe'), (' loom'), ('eye of ender') RETURNING id, name; -- returnerer:-- id | navn--------------------------------------------------+-------- -------- 1cfaae8c-61ff-4e82-a656-99263b7dd0ae | træøkse-- be043a89-a51b-4d8b-8378-699847113d46 | væve-- 927d52eb-c175-4a97-a0b2-7b7e81d9bc8e | endeøje 

Indsæt, hvis det ikke eksisterer, opdater ellers

I Postgres 9.5 og nyere kan du upsert direkte ved at bruge ON CONFLICT-konstruktionen:

CREATE TABLE parametre (nøgle TEXT PRIMARY KEY, værdi TEXT);-- når "nøgle" forårsager en overtrædelse af begrænsninger, skal du opdatere "værdi"INSERT INTO-parametrene (nøgle, værdi) VALUES ('port', ' 5432')ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value; 

Kopiér rækker fra en tabel til en anden

INSERT-sætningen har en form, hvor værdierne kan leveres af en SELECTstatement. Brug dette til at kopiere rækker fra en tabel til en anden:

-- kopier mellem tabeller med lignende kolonner INSERT INTO pending_questsSELECT * FRA quests WHERE progress <100;-- angiv nogle værdier fra en anden tabel, nogle direkte INSERT INTO archived_quests SELECT now() AS archival_date, * FROM quests WHERE afsluttet; 

Hvis du ønsker at masseindlæse tabeller, så tjek også COPY-kommandoen, som kan bruges til at indsætte rækker fra en tekst- eller CSV-fil.

Slet og returner slettede oplysninger

Du kan bruge RETURNING klausul for at returnere værdier fra rækkerne, der blev slettet ved hjælp af en bulk-delete-sætning:

-- returner listen over kunder, hvis licenser blev slettet efter udløbDELETE FROM licenser WHERE now()> udløbsdato RETURNING customer_name; 

Flyt rækker fra ét bord til et andet

Du kan flytte rækker fra en tabel til en anden i en enkelt sætning ved at bruge CTE'er med DELETE .. RETURNING :

-- flyt ting, der endnu ikke er startet, fra 2020 til 2021WITH ah_well AS (SLET FRA todos_2020 WHERE NOT started RETURNING *)INSERT INTO todos_2021 SELECT * FROM ah_well; 

Opdater rækker og returner opdaterede værdier

RETURNING-klausulen kan også bruges i OPDATERINGER. Bemærk, at kun de nye værdier af de opdaterede kolonner kan returneres på denne måde.

-- giv tilfældige mængder af mønter til kvalificerede spillere OPDATERING spillere SET coins =coins + (100 * random())::integer WHERE eligibleRETURNING id, coins; 

Hvis du har brug for den oprindelige værdi af de opdaterede kolonner:det er muligt gennem selv-join, men der er ingen garanti for atomicitet. Prøv at bruge en SELECT .. FORUPDATE i stedet.

Opdater et par tilfældige rækker og returner de opdaterede

Sådan kan du vælge et par tilfældige rækker fra en tabel, opdatere dem og returnere de opdaterede, alt sammen på én gang:

MED lucky_few AS (SELECT id FROM players ORDER BY random() LIMIT 5) OPDATERING spillere SET bonus =bonus + 100 WHERE id IN (SELECT id FROM lucky_few)RETURNING id; 

Opret en tabel ligesom en anden tabel

Brug CREATE TABLE .. LIKE-konstruktionen til at oprette en tabel med de samme kolonner som en anden:

OPRET TABEL, der skal_revideres (LIKE køb); 

Som standard opretter dette ikke lignende indekser, begrænsninger, standardindstillinger osv. For at gøre det, spørg Postgres eksplicit:

OPRET TABEL, der skal_revideres (SOM køb HERUNDER ALLE); 

Se den fulde syntaks her.

Udtræk et tilfældigt sæt rækker i en anden tabel

Siden Postgres 9.5 er TABLESAMPLE-funktionen tilgængelig til at udtrække en prøve af rækker fra en tabel. Der er to prøveudtagningsmetoder i øjeblikket, og bernoulli er normalt den du ønsker:

-- kopier 10 % af dagens køb til en anden tabelINSERT INTO to_be_audited SELECT * FROM purchasesTABLESAMPLE bernoulli(10) WHERE transaktionsdato =CURRENT_DATE; 

systemet tabelsampling-metoden er hurtigere, men returnerer ikke en ensartet fordeling. Se dokumenterne for mere information.

Opret en tabel fra en udvalgt forespørgsel

Du kan bruge CREATE TABLE .. AS-konstruktionen til at oprette tabellen og udfylde den fra en SELECT-forespørgsel, alt på én gang:

OPRET TABEL, der skal_revideres SOM SELECT * FRA køb TABLESAMPLE bernoulli(10) WHERE transaktionsdato =CURRENT_DATE; 

Den resulterende tabel er som en materialiseret visning uden en forespørgsel forbundet med den. Læs mere om CREATE TABLE .. AS her.

Opret uloggede tabeller

Ulogget tabeller er ikke understøttet af WAL-poster. Det betyder, at opdateringer og sletninger til sådanne tabeller er hurtigere, men de er ikke nedbrudstolerante og kan ikke replikeres.

OPRET ULOGGET TABEL report_20200817 (LIKE report_v3); 

Opret midlertidige tabeller

Midlertidig tabeller er implicit uloggede tabeller med en kortere levetid. De destruerer automatisk i slutningen af ​​en session (standard) eller ved slutningen af ​​transaktionen.

Data i midlertidige tabeller kan ikke deles på tværs af sessioner. Flere sessioner kan oprette midlertidige tabeller med samme navn.

-- temp-tabel for varigheden af ​​sessionenCREATE TEMPORARY TABLE scratch_20200817_run_12 (LIKE report_v3);-- temp-tabel, der vil selvdestruere efter aktuel transaktionCREATE MIDLERTIDIG TABEL scratch_20200817_run_vMIT-tabel-tempo-tabel - LIKE; der vil TRUNCATE sig selv efter den aktuelle transaktionCREATE MIDLERTIDIG TABEL scratch_20200817_run_12 (LIKE report_v3) ON COMMIT DELETE ROWS; 

Tilføj kommentarer

Kommentarer kan tilføjes til ethvert objekt i databasen. Mange værktøjer, inklusive pg_dump, forstår disse. En nyttig kommentar kan måske bare undgå et væld af besværlig oprydning!

KOMMENTAR PÅ INDEX idx_report_last_updated IS 'nødvendig for den natlige rapportapp, der kører i dc-03';KOMMENTAR TIL TRIGGER tgr_fix_column_foo IS 'dæmper effekten af ​​fejl #4857'; 

Rådgivende låse

Rådgivende låse kan bruges til at koordinere handlinger mellem to apps, der er forbundet til den samme database. Du kan bruge denne funktion til at implementere en global, distribueret mutex for en bestemt operation, for eksempel. Læs alt om det her i dokumentet.

-- klient 1:erhverve en lås SELECT pg_advisory_lock(130);-- ... gør arbejde ...SELECT pg_advisory_unlock(130);-- klient 2:forsøger at gøre det samme, men gensidigt eksklusiv-- med klient 1SELECT pg_advisory_lock(130); -- blokerer, hvis nogen anden har holdt lås med id 130-- kan også gøre det uden at blokere:SELECT pg_try_advisory_lock(130);-- returnerer falsk hvis låsen holdes af en anden klient-- ellers anskaffer låsen og returnerer derefter sand 

Aggregér i arrays, JSON-arrays eller strenge

Postgres giver aggregerede funktioner, der sammenkæder værdier i en GROUP toyield-arrays, JSON-arrays eller strenge:

-- få navne på hver guild, med en række id'er af spillere, der-- tilhører det guild. SELECT guilds.name AS guild_name, array_agg(players.id) SOM spillere FRA guilds JOIN spillere PÅ spillere. guild_id =guilds.idGROUP BY guilds.id;-- samme men spillerlisten er en CSV-streng VÆLG guilds.name, string_agg(players.id, ',') -- ... -- samme men spillerlisten er en JSONB array SELECT guilds.name, jsonb_agg(players.id) -- ... -- samme men returnerer et flot JSONB-objekt som sådan:-- { guild1:[ playerid1, playerid2, .. ], .. }SELECT jsonb_object_agg( guild_name, players) FROM ( SELECT guilds.name AS guild_name, array_agg(players.id) AS spillere FRA guilds JOIN players ON players.guild_id =guilds.idGROUP BY guilds.id) AS q; 

Aggregater med ordre

Mens vi er på emnet, kan du her se, hvordan du indstiller rækkefølgen af ​​værdier, der overføres til den samlede funktion inden for hver gruppe :

-- hver stat med en liste over amter sorteret alfabetisk SELECT states.name, string_agg(counties.name, ',' ORDER BY counties.name) FRA stater JOIN counties JOIN states.name =counties.state_name GRUPPE EFTER states.name;

Ja, der er en efterfølgende ORDER BY-klausul inde i funktionskaldet paranthesis. Ja, syntaksen er mærkelig.

Array og Unnest

Brug ARRAY-konstruktøren til at konvertere et sæt rækker, hver med en kolonne, til et array. Databasedriveren (som JDBC) burde være i stand til at kortlægge Postgres-arrays i native arrays og kan være lettere at arbejde med.

-- konverter rækker (med 1 kolonne hver) til en 1-dimensionel arraySELECT ARRAY(SELECT id FROM players WHERE lifetime_spend> 10000); 

Unnest-funktionen gør det omvendte - den konverterer hvert element i et array til en pil. De er mest nyttige ved krydssammenføjning med en liste over værdier:

VÆLG materialer.navn || ' ' || våben.navn FRA våbenCROSS JOIN UNNEST('{"træ","guld","sten","jern","diamant"}'::tekst[]) AS materialer(navn);-- returnerer:-- ? søjle?-- ------------------- træsværd-- træøkse-- træhakke-- træskovl-- guldsværd-- guldøkse-- (.. snip..)

Kombiner udvalgte erklæringer med Union

Du kan bruge UNION-konstruktionen til at kombinere resultaterne fra flere lignende SELECT'er:

VÆLG navn FRA våbenUNIONVÆLG navn FRA værktøjerUNIONVÆLG navn FRA materialer; 

Brug CTE'er til at behandle det kombinerede resultat yderligere:

WITH fight_equipment AS (VÆLG navn, skade FRA våben UNION SELECT navn, skade FRA værktøjer) VÆLG navn, skade FRA fight_equipmentORDER BY damage DESC LIMIT 5; 

Der er også INTERSECT og EXCEPT konstruktioner, på samme måde som UNION. Læs mere om disse klausuler i dokumentet.

Hurtige rettelser i Select:case, coalesce og nullif

CASE, COALESCE og NULLIF for at lave små hurtige "fixes" for SELECTED data.CASE er ligesom switch på C-lignende sprog:

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

COALESCE kan bruges til at erstatte en bestemt værdi i stedet for NULL.

-- brug en tom streng, hvis ip ikke er tilgængeligSELECT nodename, COALESCE(ip, '') FROM nodes;-- prøv at bruge den første tilgængelige, ellers brug '?'SELECT nodename, COALESCE(ipv4, ipv6, værtsnavn, '?') FRA noder;

NULLIF fungerer på den anden måde, og lader dig bruge NULL i stedet for en bestemt værdi:

-- brug NULL i stedet for '0.0.0.0'SELECT nodename, NULLIF(ipv4, '0.0.0.0') FROM noder; 

Generer tilfældige og sekventielle testdata

Forskellige metoder til at generere tilfældige data:

-- 100 tilfældige terningkastSELECT 1+(5 * random())::int FROM gener_series(1, 100);-- 100 tilfældige tekststrenge (hver 32 tegn lang)SELECT md5(random() ::tekst) FRA generere_serier(1, 100);-- 100 tilfældige tekststrenge (hver 36 tegn lang) SELECT uuid_generate_v4()::tekst FRA generere_serier(1, 100);-- 100 tilfældige små tekststrenge af varierende længdeCREATE EXTENSION IF NOT EXISTS "pgcrypto";SELECT gen_random_bytes(1+(9*random())::int)::text FROM gener_series(1, 100);-- 100 tilfældige datoer i 2019VÆLG DATO(DATO '2019-01-01 ' + ((random()*365)::int || 'days')::interval ) FROM generere_serier(1, 100); -- 100 tilfældige 2-kolonne data:1. kolonne heltal og 2. kolonne streng MED a AS ( SELECT ARRAY(SELECT random() FROM gener_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);-- et dagligt antal for 2020, generelt stigende over tidSELECT i, ( (5+random()) * (row_number() over()) ::int FROM gener_series(DATE '2020-01-01', DATE '2020-12-31', INTERVAL '1 day') AS s(i);

Brug bernoulli tabelsampling for at vælge et tilfældigt antal rækker fra en tabel:

-- vælg 15 % af rækkerne fra tabellen, valgt tilfældigt VÆLG * FRA købSTABLESAMPLE bernoulli(15) 

Brug generate_series for at generere sekventielle værdier af heltal, datoer og andre trinvise indbyggede typer:

-- generer heltal fra 1 til 100SELECT gener_series(1, 100);-- kald den genererede værditabel som "s" med en kolonne "i", for at bruge in-- CTEs og JOINsSELECT i FROM gener_series (1, 100) AS s(i);-- generer multipla af 3 på forskellige måder.VÆLG 3*i FRA generere_serier(1, 100) AS s(i);VÆLG generere_serier(1, 100, 3);-- fungerer med også datoer:her er alle mandage i 2020:SELECT gener_series(DATO '2020-01-06', DATO '2020-12-31', INTERVAL '1 uge'); 

Få omtrentlig rækkeantal

Den forfærdelige præstation af COUNT(*) er måske det grimmeste biprodukt af Postgres’ arkitektur. Hvis du bare har brug for et omtrentligt antal rækker for en kæmpebord, kan du undgå et helt ANTAL ved at forespørge statistiksamleren:

VÆLG relname, n_live_tup FROM pg_stat_user_tables; 

Resultatet er nøjagtigt efter en ANALYSE, og vil gradvist være forkert, efterhånden som rækkerne ændres. Brug ikke dette, hvis du ønsker nøjagtige optællinger.

Intervaltype

intervallet type kan ikke kun bruges som en kolonnedatatype, men kan tilføjes og trækkes fra dato og tidsstempel værdier:

-- få licenser, der udløber inden for de næste 7 dageSELECT id FROM licenser WHERE expiry_date MELLEM nu() - INTERVAL '7 dage' OG nu(); -- forlænge udløbsdatoUPDATE-licenser SET expiry_date =expiry_date + INTERVAL '1 år' WHERE id =42;

Slå begrænsningsvalidering fra for bulkinsert

-- tilføj en begrænsning, sat som "ikke gyldig"ALTER TABLE-spillere TILFØJ BEGRÆNSNING fk__players_guilds UDENLANDSKE NØGLE (guild_id) REFERENCER guilds(id) IKKE GYLDIG;-- indsæt masser af rækker i tableCOPY-spillerne FRA '/ data/players.csv' (FORMAT CSV);-- valider nu hele tabellen ALTER TABLE spillere VALIDATE CONSTRAINT fk__players_guilds; 

Dump en tabel eller forespørgsel til en CSV-fil

-- dump indholdet af en tabel til en CSV-formatfil på serveren COPY players TO '/tmp/players.csv' (FORMAT CSV);-- "header" tilføjer en overskrift med kolonnenavneCOPY players TO '/tmp/players.csv' (FORMAT CSV, HEADER);-- brug kommandoen psql til at gemme på din lokale maskine\copy players TO '~/players.csv' (FORMAT CSV);-- kan bruge en forespørgsel i stedet for af et bordnavn\kopi (VÆLG id, navn, score FRA spillere) TIL '~/players.csv' (FORMAT CSV ); 

Brug flere indbyggede datatyper i dit skemadesign

Postgres kommer med mange indbyggede datatyper. Ved at repræsentere de data, din applikation har brug for ved hjælp af en af ​​disse typer, kan du spare masser af applikationskode, gøre din udvikling hurtigere og resultere i færre fejl.

For eksempel, hvis du repræsenterer en persons placering ved hjælp af datatypenpoint og et område af interesse som en polygon , kan du tjekke om personen er i regionen blot med:

-- @>-operatøren tjekker, om området af interesse (en "polygon") indeholder-- personens placering (et "punkt") SELECT roi @> person_location FROM live_tracking; 

Her er nogle interessante Postgres-datatyper og links til, hvor du kan finde mere information om dem:

  • C-lignende enum-typer
  • Geometriske typer – punkt, kasse, linjestykke, linje, sti, polygon, cirkel
  • IPv4-, IPv6- og MAC-adresser
  • Intervaltyper – heltal-, dato- og tidsstempelintervaller
  • Arrays, der kan indeholde værdier af enhver type
  • UUID – hvis du har brug for at bruge UUID'er eller bare skal arbejde med 129-byte tilfældige heltal, så overvej at bruge uuid type og uuid-oscp udvidelse til lagring, generering og formatering af UUID'er
  • Dato- og tidsintervaller ved brug af INTERVAL-typen
  • og selvfølgelig den altid populære JSON og JSONB

Bundlede udvidelser

De fleste Postgres-installationer inkluderer en masse standard "udvidelser". Udvidelser er komponenter, der kan installeres (og rent afinstalleres), der giver funktionalitet, der ikke er inkluderet i kernen. De kan installeres pr. database.

Nogle af disse er ret nyttige, og det er værd at bruge lidt tid på at lære dem at kende:

  • pg_stat_statements – statistik vedrørende udførelsen af ​​hver SQL-forespørgsel
  • auto_explain – log forespørgselsudførelsesplanen for (langsomme) forespørgsler
  • postgres_fdw,dblink andfile_fdw – måder at få adgang til andre datakilder (såsom eksterne Postgres-servere, MySQL-servere, filer på serverens filsystem) som almindelige tabeller
  • citext – en datatype, der ikke skelner mellem store og små bogstaver, mere effektiv end lavere()-ing overalt
  • hstore – en nøgle-værdidatatype
  • pgcrypto –SHA-hash-funktioner, kryptering

  1. SQL Server 2017 installation

  2. Oracle DBA Mentor

  3. Django JSONField-filtrering

  4. Forskellen mellem oracle DATE og TIMESTAMP