Usando PostgreSQL v9.1. Tengo las siguientes tablas:
CREATE TABLE foo
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
type VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE bar
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
description VARCHAR(40) NOT NULL UNIQUE,
foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);
Digamos que la primera tabla foose rellena así:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
¿Hay alguna forma de insertar filas barfácilmente haciendo referencia a la footabla? ¿O debo hacerlo en dos pasos, primero buscando el footipo que quiero y luego insertando una nueva fila bar?
Aquí hay un ejemplo de pseudocódigo que muestra lo que esperaba que se pudiera hacer:
INSERT INTO bar (description, foo_id) VALUES
( 'testing', SELECT id from foo WHERE type='blue' ),
( 'another row', SELECT id from foo WHERE type='red' );
postgresql
foreign-key
postgresql-9.1
insert
Stéphane
fuente
fuente

INSERTAR llano
El uso de a en
LEFT [OUTER] JOINlugar de[INNER] JOINsignifica que las filas devalno se descartan cuando no se encuentra ninguna coincidenciafoo. En cambio,NULLse ingresa parafoo_id.La
VALUESexpresión en la subconsulta hace lo mismo que el CTE de @ ypercube . Las expresiones de tabla comunes ofrecen características adicionales y son más fáciles de leer en grandes consultas, pero también se presentan como barreras de optimización. Por lo tanto, las subconsultas suelen ser un poco más rápidas cuando no se necesita ninguna de las anteriores.idcomo nombre de columna es un antipatrón generalizado. En caso de serfoo_idybar_iddescriptiva o nada. Al unir un montón de tablas, terminas con múltiples columnas todas llamadasid...Considere simple
texto envarcharlugar devarchar(n). Si realmente necesita imponer una restricción de longitud, agregue unaCHECKrestricción:Es posible que deba agregar conversiones de tipo explícito. Dado que la
VALUESexpresión no se adjunta directamente a una tabla (como enINSERT ... VALUES ...), los tipos no se pueden derivar y los tipos de datos predeterminados se usan sin una declaración de tipo explícita, que puede no funcionar en todos los casos. Es suficiente hacerlo en la primera fila, el resto se alineará.INSERTAR filas FK faltantes al mismo tiempo
Si desea crear entradas inexistentes
foosobre la marcha, en una sola instrucción SQL , los CTE son instrumentales:Tenga en cuenta las dos nuevas filas ficticias para insertar. Ambos son de color púrpura , que aún no existe
foo. Dos filas para ilustrar la necesidadDISTINCTen la primeraINSERTdeclaración.Explicación paso a paso
El primer CTE
selproporciona múltiples filas de datos de entrada. La subconsultavalcon laVALUESexpresión se puede reemplazar con una tabla o subconsulta como fuente. InmediatamenteLEFT JOINparafooagregarfoo_idlastypefilas preexistentes . Todas las demás filas se ponen defoo_id IS NULLesta manera.El segundo CTE
insinserta nuevos tipos distintos (foo_id IS NULL)fooy devuelve el recién generadofoo_id, junto con eltypepara volver a unir e insertar filas.El exterior final
INSERTahora puede insertar un foo.id para cada fila: ya sea el tipo preexistente o se insertó en el paso 2.Estrictamente hablando, ambas inserciones ocurren "en paralelo", pero dado que esta es una declaración única , las
FOREIGN KEYrestricciones predeterminadas no se quejarán. La integridad referencial se aplica al final de la declaración por defecto.SQL Fiddle para Postgres 9.3. (Funciona igual en 9.1.)
Hay una pequeña condición de carrera si ejecuta varias de estas consultas al mismo tiempo. Lea más en preguntas relacionadas aquí y aquí y aquí . Realmente solo ocurre bajo una gran carga concurrente, si alguna vez. En comparación con las soluciones de almacenamiento en caché como se anuncia en otra respuesta, la posibilidad es muy pequeña .
Función para uso repetido
Para uso repetido, crearía una función SQL que toma una matriz de registros como parámetro y la usa
unnest(param)en lugar de laVALUESexpresión.O, si la sintaxis para las matrices de registros es demasiado complicada para usted, use una cadena separada por comas como parámetro
_param. Por ejemplo de la forma:Luego use esto para reemplazar la
VALUESexpresión en la declaración anterior:Función con UPSERT en Postgres 9.5
Cree un tipo de fila personalizado para pasar parámetros. Podríamos prescindir de él, pero es más simple:
Función:
Llamada:
Rápido y sólido como una roca para entornos con transacciones concurrentes.
Además de las consultas anteriores, esto ...
... aplica
SELECToINSERTactivafoo:typese inserta todo lo que no existe en la tabla FK. Asumiendo que la mayoría de los tipos preexisten. Para estar absolutamente seguro y descartar las condiciones de carrera, las filas existentes que necesitamos están bloqueadas (para que las transacciones concurrentes no puedan interferir). Si eso es demasiado paranoico para su caso, puede reemplazar:con
... aplica
INSERToUPDATE(verdadero "UPSERT") enbar: Si eldescriptionya existe,typese actualiza:Pero solo si
typerealmente cambia:... pasa valores como tipos de fila bien conocidos con un
VARIADICparámetro. ¡Tenga en cuenta el máximo predeterminado de 100 parámetros! Comparar:Hay muchas otras formas de pasar varias filas ...
Relacionado:
fuente
INSERT missing FK rows at the same timeejemplo, ¿poner esto en una transacción reduciría el riesgo de condiciones de carrera en SQL Server?SELECTdentro de unaWITHcláusula). Fuente: documentación de la EM.INSERT ... RETURNING \gsetypsqlluego usar los valores devueltos como psql:'variables', pero esto solo funciona para inserciones de una sola fila.Buscar. Básicamente, necesitas los identificadores para insertarlos en la barra.
No postgres específico, por cierto. (y no lo etiquetó así): en general, así es como funciona SQL. No hay atajos aquí.
Sin embargo, en cuanto a la aplicación, es posible que tenga una memoria caché de elementos foo en la memoria. Mis tablas a menudo tienen hasta 3 campos únicos:
Ejemplo:
Obviamente, cuando desea vincular algo a una cuenta, primero debe obtener técnicamente el Id, pero dado que tanto el Identificador como el Código nunca cambian una vez que están allí, un caché positivo en la memoria puede evitar que la mayoría de las búsquedas lleguen a la base de datos.
fuente