Insertar una sola fila en una tabla es lo que viene a la mente cuando piensa en la declaración INSERT en PostgreSQL. ¡Sin embargo, tiene algunos trucos más bajo la manga! Siga leyendo para descubrir algunas de las cosas más interesantes que puede hacer con INSERT.
Copiando en lote
Supongamos que desea capturar periódicamente instantáneas de una tabla:todas las filas de la tabla deben copiarse en otra tabla, con una columna de marca de tiempo adicional que indique cuándo se tomó la instantánea. Así es como puede crear y completar la tabla la primera 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)
Y a partir de ahí, puedes usar el INSERT..SELECT
forma de instrucción INSERT para copiar filas de una tabla e insertarlas en otra. También puede completar valores adicionales en la fila de la tabla 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
En PostgreSQL 9.5, el ON CONFLICT
se agregó una cláusula a INSERT. Esto permite a los desarrolladores de aplicaciones escribir menos código y hacer más trabajo en SQL.
Aquí hay una tabla de claves, pares de valores:
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Un caso de uso común es insertar una fila solo si no existe, y si existe, no sobrescribir. Esto se hace con ON CONFLICT..DO NOTHING
cláusula de la sentencia 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)
Otro uso común es insertar una fila si no existe y actualizar el valor, si existe. Esto se puede hacer con 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)
En el primer caso anterior, el valor de 'host' se sobrescribió con el nuevo valor, y en el segundo caso, el valor de 'ssl' se insertó como la tercera fila.
Se pueden realizar casos de uso aún más sofisticados con DO UPDATE
. Considere la siguiente tabla, donde además de la clave y el valor, hay una columna llamada "acumular". Para las filas donde acumular es verdadero, los valores deben acumularse como una cadena separada por comas. Para otras filas, los valores son de un solo valor.
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)
El WHERE
La cláusula se puede usar para sobrescribir la columna "valor" o agregarla, según el valor de "acumular", así:
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)
La primera declaración no acumuló el valor de '3306' en 'puerto' porque 'acumular' estaba desactivado para esa fila. Las siguientes dos declaraciones agregaron los valores '127.0.0.1' y '10.0.10.1' al valor de 'escuchar', porque 'acumular' era verdadero.
Devolución de valores generados
Los valores generados por PostgreSQL durante la inserción, como los valores predeterminados o los valores SERIE autoincrementados, se pueden devolver usando el RETURNING
cláusula de la sentencia INSERT.
Suponga que necesita generar UUID aleatorios como claves para las filas de una tabla. Puede dejar que PostgreSQL haga el trabajo de generar los UUID y hacer que le devuelva el valor generado de esta manera:
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)
Mover filas con cláusulas CTE
Incluso puede mover filas entre tablas con INSERTAR, usando WITH
cláusula. Aquí hay dos tablas con listas de tareas para diferentes años.
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 los elementos pendientes que aún no se completaron en 2018 a 2019, básicamente puede eliminar dichas filas de la tabla de 2018 e insertarlas en la tabla de 2019 de una sola 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 obtener más información sobre la pequeña e inteligente declaración INSERT, consulte la documentación y experimente.