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

PostgreSQL:Den alsidige INSERT

At indsætte en enkelt række i en tabel er det, der kommer til at tænke på, når du tænker på INSERT-sætningen i PostgreSQL. Den har dog et par flere tricks i ærmet! Læs videre for at opdage nogle af de mere interessante ting, du kan lave med INSERT.

Kopiering i bulk

Lad os sige, at du med jævne mellemrum vil fange snapshots af en tabel - alle rækker i tabellen skal kopieres til en anden tabel, med en ekstra tidsstempelkolonne, der angiver, hvornår snapshottet blev taget. Sådan kan du oprette og udfylde tabellen første gang:

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)

Og fra da af kan du bruge INSERT..SELECT form af INSERT-sætning for at kopiere rækker fra en tabel og indsætte i en anden. Du kan også udfylde ekstra værdier i destinationstabelrækken.

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 er ON CONFLICT klausul blev tilføjet til INSERT. Dette lader applikationsudviklere skrive mindre kode og udføre mere arbejde i SQL.

Her er en tabel med nøgleværdipar:

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

En almindelig anvendelse er kun at indsætte en række, hvis den ikke eksisterer - og hvis den gør, skal du ikke overskrive. Dette gøres med ON CONFLICT..DO NOTHING klausul i INSERT-sætningen:

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 anden almindelig brug er at indsætte en række, hvis den ikke eksisterer, og opdatere værdien, hvis den gør. Dette kan gøres 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ørste tilfælde ovenfor blev værdien af ​​'host' overskrevet med den nye værdi, og i det andet tilfælde blev værdien af ​​'ssl' indsat som den tredje række.

Endnu mere sofistikerede use cases kan realiseres med DO UPDATE . Overvej tabellen nedenfor, hvor der udover nøgle og værdi er en kolonne kaldet "akkumuler". For rækker, hvor akkumulering er sand, er det meningen, at værdierne skal akkumuleres som en kommasepareret streng. For andre rækker er værdier med en enkelt værdi.

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 klausulen kan bruges til enten at overskrive "værdi"-kolonnen eller tilføje den, afhængigt af værdien af ​​"akkumulere", sådan her:

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)

Den første erklæring akkumulerede ikke værdien af ​​'3306' i 'port', fordi 'akkumuler' var slået fra for den række. De næste to udsagn tilføjede værdierne '127.0.0.1' og '10.0.10.1' til værdien af ​​'lyt', fordi 'akkumuler' var sandt.

Returnerende genererede værdier

Værdier genereret af PostgreSQL under indsættelse, som standardværdier eller autoinkrementerede SERIAL-værdier, kan returneres ved hjælp af RETURNING klausul i INSERT-sætningen.

Antag, at du skal generere tilfældige UUID'er som nøgler til rækker i en tabel. Du kan lade PostgreSQL udføre arbejdet med at generere UUID'erne og få det til at returnere den genererede værdi til dig sådan her:

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)

Flytning af rækker med CTE-klausuler

Du kan endda flytte rækker mellem tabeller med INSERT ved at bruge WITH klausul. Her er to tabeller med todo-lister for forskellige å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)

For at flytte de todo-punkter, der endnu ikke er afsluttet i 2018 til 2019, kan du stort set slette sådanne rækker fra 2018-tabellen og indsætte dem i 2019-tabellen på én gang:

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)

For at lære mere om den smarte lille INSERT-sætning, tjek dokumentationen og eksperimenter!


  1. PHP (MySQL) fejl:Advarsel:mysql_num_rows() forventer, at parameter 1 er ressource

  2. Hvordan listes aktive/åbne forbindelser i Oracle?

  3. Tips til lagring af PostgreSQL-sikkerhedskopier på Google Cloud (GCP)

  4. Tips til opgradering af Percona XtraDB Cluster til 8.0