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
iddetblsi la "fila" existe. -
El tercer CTE i inserta la "fila" en
tblsi (y solo si) no existe, devolviendoid. -
El
SELECTfinal devuelve elid. Agregué una columnasrcindicando la "fuente" - si la "fila" existía previamente yidproviene 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
tbly 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?