Una solución en una sola instrucción SQL. Requiere PostgreSQL 8.4 o posterior.
Considere la siguiente demostración:
Configuración de prueba:
CREATE TEMP TABLE tbl (
id serial PRIMARY KEY
,txt text UNIQUE -- obviously there is unique column (or set of columns)
);
INSERT INTO tbl(txt) VALUES ('one'), ('two');
Comando INSERTAR/SELECCIONAR:
WITH v AS (SELECT 'three'::text AS txt)
,s AS (SELECT id FROM tbl JOIN v USING (txt))
,i AS (
INSERT INTO tbl (txt)
SELECT txt
FROM v
WHERE NOT EXISTS (SELECT * FROM s)
RETURNING id
)
SELECT id, 'i'::text AS src FROM i
UNION ALL
SELECT id, 's' FROM s;
-
El primer V CTE no es estrictamente necesario, pero logra que tengas que ingresar tus valores solo una vez.
-
El segundo CTE s selecciona el
id
detbl
si la "fila" existe. -
El tercer CTE i inserta la "fila" en
tbl
si (y solo si) no existe, devolviendoid
. -
El
SELECT
final devuelve elid
. Agregué una columnasrc
indicando la "fuente" - si la "fila" existía previamente yid
proviene de un SELECT, o la "fila" era nueva y también lo es elid
. -
Esta versión debería ser lo más rápida posible ya que no necesita un SELECT adicional de
tbl
y usa los CTE en su lugar.
Para que esto sea seguro contra posibles condiciones de carrera en un entorno multiusuario:
También para técnicas actualizadas usando el nuevo UPSERT en Postgres 9.5 o posterior:
- ¿Es SELECCIONAR o INSERTAR en una función propensa a condiciones de carrera?