INSERT utilizando los resultados de CTE INSERT para proporcionar valores de identificación únicos


8

Estoy escribiendo un trabajo para transformar datos de un diseño antiguo en uno nuevo. En este proceso, necesito tomar la identificación de una inserción en una tabla separada y usarla en una inserción en la tabla de destino, como tal:

CREATE TABLE t1 {
  t1_id BIGSERIAL,
  col1 VARCHAR
};
CREATE TABLE t2 {
  t2_id BIGSERIAL,
  col2 VARCHAR, -- renamed from col1 to avoid confusion
  t1_id BIGINT REFERENCES t1.t1_id
};

Tengo el SQL definido que coincide con la siguiente forma:

WITH ins AS (
  INSERT INTO t1 (t1_id) VALUES (DEFAULT) RETURNING t1_id
) INSERT INTO t2
  (col1, t1_id)
SELECT
  a.val1, (SELECT * FROM ins)
FROM t3 a;

Quería que esto se ejecutara SELECT * FROM inspara cada fila del SELECT... pero en su lugar solo se ejecuta una vez y usa ese valor para todas las filas en el SELECT. ¿Cómo puedo reestructurar mi SQL para obtener el comportamiento deseado?

edit4

t1 termina pareciéndose a:

1,<NULL>
(1 row)

t2 termina pareciéndose a:

10,'a',1
11,'b',1 -- problem with id from t1 being 1
12,'c',1 -- problem with id from t1 being 1
.
.

Cómo quiero que se vea t1:

1,<NULL>
2,<NULL>
3,<NULL>
.
.

Cómo quiero que se vea t2:

10,'a',1
11,'b',2 -- id from t1 of 2
12,'c',3 -- id from t1 of 3
.
.

editar Para abordar lo que dijo a_horse_with_no_name, también probé esto (con el mismo resultado):

WITH ins AS (
  INSERT INTO t1 (t1_id) VALUES (DEFAULT) RETURNING t1_id
) INSERT INTO t2
  (col1, t1_id)
SELECT
  a.val1, b.t1_id
FROM t3 a
JOIN ins b ON TRUE;

edit2 Intenté hacer referencia directa al apropiado SEQUENCEen mi consulta, y eso SÍ funciona, pero no me gusta mucho esa solución (principalmente porque no me gusta codificar nombres de objetos). Si hay CUALQUIER solución, otra que hacer referencia directa al nombre del SEQUENCElo agradecería. :)

edit3 Supongo que otra solución sería hacer uso de un PROCEDUREpara hacer el en INSERTlugar de un CTE ... pero aún así agradecería las opciones / sugerencias.


1
Necesitas unirte insyt3
a_horse_with_no_name

Intenté eso también y todavía solo calculó el valor solo una vez. Pero tal vez no tenía mi unión bastante correcta. Editaré mi publicación para mostrar lo que intenté con eso.
Joishi Bodio

1
Está insertando solo una fila t1y no proporciona ningún valor para t1.col1. ¿De dónde deben venir los datos para esa columna? ¿Está t1.col1relacionado con t2.col1?
ypercubeᵀᴹ

ypercube - t1.col1 puede ser NULL y se insertará en un proceso posterior. Debido a que estaba haciendo referencia al CTE como SUBSELECTO en los valores de fila reales, pensé que se ejecutaría más de una vez, pero resulta que era incorrecto en esa suposición ... y es por eso que hago esta pregunta aquí. Ya he intentado buscar una respuesta en Google durante las últimas horas y aún no he podido encontrar lo que es correcto. Y no, t1.col1 no está relacionado con t2.col1 ... perdón por esa confusión.
Joishi Bodio

1
Aún así, INSERT INTO t1 (t1_id) VALUES (DEFAULT)inserta solo 1 fila en t1. Por lo tanto, no importa si pones el insen la FROMcláusula y lo unes t3o no. ¿Puede mostrarnos cómo insertaría 2 (o más) filas t1? Y lo que es más importante, ¿cómo sabe cuál de los 2 (o más) t1.idvalores coincidiría con las filas insertadas t2?
ypercubeᵀᴹ

Respuestas:


8

No entiendo por qué necesitas 2 tablas si solo tienen una relación de 1-1. Pero aquí está ( pkes la clave principal de t3):

WITH ins AS (
  INSERT INTO t1 (col1) 
    SELECT NULL FROM t3 
  RETURNING t1_id
) 
, r AS
( SELECT t1_id, ROW_NUMBER() OVER () AS rn
  FROM ins
) 
, t AS
( SELECT *, ROW_NUMBER() OVER () AS rn
  FROM t3
) 
INSERT INTO t2
  (col1, t1_id)
SELECT
  t.val1, r.t1_id
FROM t 
  JOIN r USING (rn) ;

Si su t3 es el resultado de un SELECT en lugar de una tabla preexistente, puede implementarlo como tal para que no tenga que repetir la consulta t3 dos veces:

WITH t3 AS (
  SELECT ...
), ins AS (
  INSERT INTO t1 (col1)
    SELECT NULL FROM t3
  RETURNING t1_id
), r AS (
  SELECT t1_id, ROW_NUMBER() OVER () AS rn
  FROM ins
), t AS (
  SELECT *, ROW_NUMBER() OVER () AS rn
  FROM t3
) INSERT INTO t2
  (col1, t1_id)
SELECT
  t.val1, r.t1_id
FROM t 
  JOIN r USING (rn);

La razón por la que necesito las dos tablas es porque hay otra tabla que también necesitará almacenar valores en t1 ... (t1 tendrá enlaces a t2 y t4) t1 está destinado a ser una tabla de información de contacto (con teclas para dirección, correo electrónico y tablas de números de teléfono) y t2 y t4 son entidades en diferentes dominios que necesitarán tener información de contacto asociada a ellos. Puede que mi vocabulario sea incorrecto, pero eso es esencialmente el motivo. Gracias por la respuesta, lo probaré.
Joishi Bodio

Editado un error menor. Utiliza la última versión.
ypercubeᵀᴹ

OK, tiene sentido entonces. Pero es posible que no necesites t2_idnada. Parece que puedes usar el t2(t1_id)como PK de t2.
ypercubeᵀᴹ

:) Me está dando un error de sintaxis con DEFAULT en este momento, tratando de descubrir cuál podría ser. ERROR: syntax error at or near "DEFAULT" LINE 2: DEFAULT AS contact_detail_id
Joishi Bodio

Hm, parece DEFAULTque no se puede usar de esa manera. Ni regresar elt.pk
ypercubeᵀᴹ
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.