sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL:The Versatile INSERT

Att infoga en enda rad i en tabell är det som kommer att tänka på när du tänker på INSERT-satsen i PostgreSQL. Den har dock några fler knep på ärmen! Läs vidare för att upptäcka några av de mer intressanta sakerna du kan göra med INSERT.

Kopiera samtidigt

Låt oss säga att du med jämna mellanrum vill ta ögonblicksbilder av en tabell – alla rader i tabellen bör kopieras till en annan tabell, med en extra tidsstämpelkolumn som anger när ögonblicksbilden togs. Så här kan du skapa och fylla i tabellen första gången:

demo=# SELECT * FROM mytable;
 ticker | quote
--------+-------
 FOO    | $4.01
 BAR    | $1.42
(2 rows)

demo=# CREATE TABLE snaps_of_mytable AS
demo-#   SELECT current_timestamp AS snapped_at, *
demo-#     FROM mytable;
SELECT 2
demo=#
demo=# SELECT * FROM snaps_of_mytable ;
         snapped_at          | ticker | quote
-----------------------------+--------+-------
 2018-10-09 04:16:22.3613+00 | FOO    | $4.01
 2018-10-09 04:16:22.3613+00 | BAR    | $1.42
(2 rows)

Och från och med då kan du använda INSERT..SELECT form av INSERT-sats för att kopiera rader från en tabell och infoga i en annan. Du kan också fylla i extra värden i måltabellraden.

demo=# INSERT INTO snaps_of_mytable
demo-#   SELECT current_timestamp AS snapped_at, *
demo-#     FROM mytable;
INSERT 0 2
demo=#
demo=# SELECT * FROM snaps_of_mytable ;
          snapped_at           | ticker | quote
-------------------------------+--------+-------
 2018-10-09 04:16:22.3613+00   | FOO    | $4.01
 2018-10-09 04:16:22.3613+00   | BAR    | $1.42
 2018-10-09 04:18:53.432224+00 | BAR    | $1.42
 2018-10-09 04:18:53.432224+00 | FOO    | $4.10
(4 rows)

Upserts

I PostgreSQL 9.5, ON CONFLICT klausul lades till i INSERT. Detta låter applikationsutvecklare skriva mindre kod och göra mer arbete i SQL.

Här är en tabell med nyckel- och värdepar:

demo=# SELECT * FROM kv;
 key  |   value
------+-----------
 host | 127.0.0.1
 port | 5432
(2 rows)

Ett vanligt användningsfall är att bara infoga en rad om den inte finns – och om den gör det, skriv inte över. Detta görs med ON CONFLICT..DO NOTHING sats i INSERT-satsen:

demo=# INSERT INTO kv (key, value) VALUES ('port', '3306')
demo-# ON CONFLICT (key) DO NOTHING;
INSERT 0 0
demo=# SELECT * FROM kv;
 key  |   value
------+-----------
 host | 127.0.0.1
 port | 5432
(2 rows)

En annan vanlig användning är att infoga en rad om den inte finns och uppdatera värdet om den gör det. Detta kan göras med ON CONFLICT..DO UPDATE klausul.

demo=# INSERT INTO kv (key, value) VALUES ('host', '10.0.10.1')
demo-# ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value;
INSERT 0 1
demo=# INSERT INTO kv (key, value) VALUES ('ssl', 'off')
demo-# ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value;
INSERT 0 1
demo=# SELECT * FROM kv;
 key  |   value
------+-----------
 host | 10.0.10.1
 port | 5432
 ssl  | off
(3 rows)

I det första fallet ovan skrevs värdet av "värd" över med det nya värdet, och i det andra fallet infogades värdet av "ssl" som den tredje raden.

Ännu mer sofistikerade användningsfall kan realiseras med DO UPDATE . Tänk på tabellen nedan, där det förutom nyckel och värde finns en kolumn som heter "ackumulera". För rader där ackumulering är sant, är värdena avsedda att ackumuleras som en kommaseparerad sträng. För andra rader har värden enstaka värden.

demo=# CREATE TABLE kv2 (
demo(#     key text PRIMARY KEY,
demo(#     accumulate boolean NOT NULL DEFAULT false,
demo(#     value text
demo(# );
CREATE TABLE
demo=# INSERT INTO kv2 VAlUES
demo-#     ('port', false, '5432'),
demo-#     ('listen', true, NULL);
INSERT 0 2
demo=# SELECT * FROM kv2;
  key   | accumulate | value
--------+------------+-------
 port   | f          | 5432
 listen | t          |
(2 rows)

WHERE sats kan användas för att antingen skriva över "värde"-kolumnen eller lägga till i den, beroende på värdet på "ackumulera", så här:

demo=# INSERT INTO kv2 AS t (key, value) VALUES ('port', '3306')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 0
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('listen', '127.0.0.1')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 1
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('listen', '10.0.10.1')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 1
demo=# SELECT * FROM kv2;
  key   | accumulate |        value
--------+------------+---------------------
 port   | f          | 5432
 listen | t          | 127.0.0.1,10.0.10.1
(2 rows)

Det första uttalandet samlade inte värdet på "3306" i "port" eftersom "ackumulera" var avstängt för den raden. De följande två påståendena lade till värdena "127.0.0.1" och "10.0.10.1" till värdet för "lyssna", eftersom "ackumulera" var sant.

Returnera genererade värden

Värden som genereras av PostgreSQL under infogning, som standardvärden eller autoinkrementerade SERIELLA värden kan returneras med RETURNING satsen i INSERT-satsen.

Anta att du behöver generera slumpmässiga UUID som nycklar för rader i en tabell. Du kan låta PostgreSQL göra arbetet med att generera UUID och få det att returnera det genererade värdet till dig så här:

demo=# INSERT INTO kv (key, value) VALUES (gen_random_uuid(), 'foo') RETURNING key;
                 key
--------------------------------------
 d93ceaa5-30a8-4285-83c5-7defa79e2f90
(1 row)

INSERT 0 1
demo=# INSERT INTO kv (key, value) VALUES (gen_random_uuid(), 'bar') RETURNING key;
                 key
--------------------------------------
 caf9c5d9-9a79-4b26-877f-a75a083b0c79
(1 row)

INSERT 0 1
demo=# SELECT * FROM kv;
                 key                  | value
--------------------------------------+-------
 d93ceaa5-30a8-4285-83c5-7defa79e2f90 | foo
 caf9c5d9-9a79-4b26-877f-a75a083b0c79 | bar
(2 rows)

Flytta rader med CTE-klausuler

Du kan till och med flytta rader mellan tabeller med INSERT, med hjälp av WITH klausul. Här är två tabeller med att göra-listor för olika år.

demo=# SELECT * FROM todos_2018;
      what      | done
----------------+------
 thing to do #1 | t
 thing to do #2 | t
 thing to do #3 | f
(3 rows)

demo=# SELECT * FROM todos_2019;
 what | done
------+------
(0 rows)

För att flytta de att göra-objekt som ännu inte är slutförda under 2018 till 2019, kan du i princip ta bort sådana rader från 2018-tabellen och infoga dem i 2019-tabellen i ett steg:

demo=# WITH items AS (
demo(#     DELETE FROM todos_2018
demo(#     WHERE NOT done
demo(#     RETURNING *
demo(# )
demo-# INSERT INTO todos_2019 SELECT * FROM items;
INSERT 0 1
demo=# SELECT * FROM todos_2018;
      what      | done
----------------+------
 thing to do #1 | t
 thing to do #2 | t
(2 rows)

demo=# SELECT * FROM todos_2019;
      what      | done
----------------+------
 thing to do #3 | f
(1 row)

För att lära dig mer om den smarta lilla INSERT-satsen, kolla in dokumentationen och experimentet!


  1. SQL är inte en gruppfunktion i en grupp

  2. Hur ansluter man till MS SQL Server med Inno Setup?

  3. Hur kan jag skriva SQL i Oracle i mitt fall?

  4. SQL Truncate