PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

PostgreSQL:o INSERT versátil


Inserir uma única linha em uma tabela é o que vem à mente quando você pensa na instrução INSERT no PostgreSQL. No entanto, tem mais alguns truques na manga! Continue lendo para descobrir algumas das coisas mais interessantes que você pode fazer com INSERT.

Copiando em massa


Digamos que você queira capturar periodicamente snapshots de uma tabela – todas as linhas da tabela devem ser copiadas para outra tabela, com uma coluna de timestamp adicional indicando quando o snapshot foi tirado. Veja como você pode criar e preencher a tabela na primeira vez:
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)

E a partir de então, você pode usar o INSERT..SELECT forma de instrução INSERT para copiar linhas de uma tabela e inserir em outra. Você também pode preencher valores extras na linha da tabela de destino.
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


No PostgreSQL 9.5, o ON CONFLICT cláusula foi adicionada a INSERT. Isso permite que os desenvolvedores de aplicativos escrevam menos código e trabalhem mais em SQL.

Aqui está uma tabela de pares de chave e valor:
demo=# SELECT * FROM kv;
 key  |   value
------+-----------
 host | 127.0.0.1
 port | 5432
(2 rows)

Um caso de uso comum é inserir uma linha somente se ela não existir – e se existir, não sobrescreva. Isso é feito com o ON CONFLICT..DO NOTHING cláusula da instrução INSERT:
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)

Outro uso comum é inserir uma linha se ela não existir e atualizar o valor, se existir. Isso pode ser feito com o ON CONFLICT..DO UPDATE cláusula.
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)

No primeiro caso acima o valor de 'host' foi substituído pelo novo valor, e no segundo caso o valor de 'ssl' foi inserido como terceira linha.

Casos de uso ainda mais sofisticados podem ser realizados com DO UPDATE . Considere a tabela abaixo, onde além de chave e valor, existe uma coluna chamada “acumular”. Para linhas em que acumular é verdadeiro, os valores devem ser acumulados como uma string separada por vírgulas. Para outras linhas, os valores são de valor único.
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)

O WHERE cláusula pode ser usada para sobrescrever a coluna “valor” ou anexá-la, dependendo do valor de “acumular”, assim:
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)

A primeira instrução não acumulou o valor de '3306' em 'port' porque 'accumulate' estava desativado para essa linha. As duas declarações seguintes adicionaram os valores ‘127.0.0.1’ e ‘10.0.10.1’ ao valor de ‘ouvir’, porque ‘acumular’ era verdadeiro.

Retornando valores gerados


Valores gerados pelo PostgreSQL durante a inserção, como valores padrão ou valores SERIAL autoincrementados, podem ser retornados usando o RETURNING cláusula da instrução INSERT.

Suponha que você precise gerar UUIDs aleatórios como chaves para linhas em uma tabela. Você pode deixar o PostgreSQL fazer o trabalho de gerar os UUIDs e fazer com que ele retorne o valor gerado para você assim:
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)

Movendo linhas com cláusulas CTE


Você pode até mesmo mover linhas entre tabelas com INSERT, usando o WITH cláusula.Aqui estão duas tabelas com listas de tarefas para anos diferentes.
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)

Para mover os itens de tarefas que ainda não foram concluídos em 2018 para 2019, você pode basicamente excluir essas linhas da tabela 2018 e inseri-las na tabela 2019 de uma só vez:
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)

Para saber mais sobre a pequena instrução INSERT inteligente, confira a documentação e experimente!