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 foo
se rellena así:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
¿Hay alguna forma de insertar filas bar
fácilmente haciendo referencia a la foo
tabla? ¿O debo hacerlo en dos pasos, primero buscando el foo
tipo 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] JOIN
lugar de[INNER] JOIN
significa que las filas deval
no se descartan cuando no se encuentra ninguna coincidenciafoo
. En cambio,NULL
se ingresa parafoo_id
.La
VALUES
expresió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.id
como nombre de columna es un antipatrón generalizado. En caso de serfoo_id
ybar_id
descriptiva o nada. Al unir un montón de tablas, terminas con múltiples columnas todas llamadasid
...Considere simple
text
o envarchar
lugar devarchar(n)
. Si realmente necesita imponer una restricción de longitud, agregue unaCHECK
restricción:Es posible que deba agregar conversiones de tipo explícito. Dado que la
VALUES
expresió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
foo
sobre 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 necesidadDISTINCT
en la primeraINSERT
declaración.Explicación paso a paso
El primer CTE
sel
proporciona múltiples filas de datos de entrada. La subconsultaval
con laVALUES
expresión se puede reemplazar con una tabla o subconsulta como fuente. InmediatamenteLEFT JOIN
parafoo
agregarfoo_id
lastype
filas preexistentes . Todas las demás filas se ponen defoo_id IS NULL
esta manera.El segundo CTE
ins
inserta nuevos tipos distintos (foo_id IS NULL
)foo
y devuelve el recién generadofoo_id
, junto con eltype
para volver a unir e insertar filas.El exterior final
INSERT
ahora 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 KEY
restricciones 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 laVALUES
expresió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
VALUES
expresió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
SELECT
oINSERT
activafoo
:type
se 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
INSERT
oUPDATE
(verdadero "UPSERT") enbar
: Si eldescription
ya existe,type
se actualiza:Pero solo si
type
realmente cambia:... pasa valores como tipos de fila bien conocidos con un
VARIADIC
pará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 time
ejemplo, ¿poner esto en una transacción reduciría el riesgo de condiciones de carrera en SQL Server?SELECT
dentro de unaWITH
cláusula). Fuente: documentación de la EM.INSERT ... RETURNING \gset
ypsql
luego 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