2012-09-14 12 views
8

Tengo una tabla con una clave primaria SERIE, y también una columna ltree, cuyo valor quiero que sea la concatenación de esas claves primarias. p.ej.Valor de referencia de la columna en serie en otra columna durante el mismo INSERTAR

id | path 
---------- 
1 1 
2 1.2 
3 1.2.3 
4 1.4 
5 1.5 

Tengo curiosidad por saber si hay una manera de hacer una inserción de este tipo en una consulta, p.

INSERT INTO foo (id, ltree) VALUES (DEFAULT, THIS.id::text) 

Probablemente estoy aquí extralimitación y tratando de hacer en una consulta lo que debería estar haciendo en dos (agrupados en una transacción).

+0

Esa ruta no parece muy consultable. –

+0

Los valores de mi ruta se basan en esto: http://stackoverflow.com/a/607379/39529 –

+0

PostgreSQL tiene recursión, expresión de tabla común, mucho más fácil y más rápido que su solución de ruta/solución alternativa. –

Respuesta

7

Se podría utilizar una subconsulta o un CTE de escritura para recuperar el valor de la secuencia vez y utilizarlo repetidamente:

WITH i AS (
    SELECT nextval('foo_id_seq') AS id 
    ) 
INSERT INTO foo (id, ltree) 
SELECT id, '1.' || id 
FROM i; 

Data-modifying CTE requiere Postgres 9.1 o posterior.

Si no está seguro sobre el nombre de la secuencia se puede utilizar pg_get_serial_sequence() lugar:

WITH i AS (
    SELECT nextval(pg_get_serial_sequence('foo', 'id')) AS id 
    ) 
INSERT INTO foo (id, ltree) 
SELECT id, '1.' || id 
FROM i; 

Si el nombre de la tabla "foo" podría no ser único en todo el esquema en la base de datos, puede esquema -calificarlo. Y si la ortografía de cualquier nombre no es estándar, usted tiene que hacer doble cita:

pg_get_serial_sequence('"My_odd_Schema".foo', 'id') 



pruebas rápidas indican @Mark's idea con lastval()podía trabajo también:

INSERT INTO foo (ltree) VALUES ('1.' || lastval()); 
  • Simplemente puede dejar id fuera de la consulta, la columna serial se asignará automáticamente. No hace diferencia.

  • No debe haber una condición de carrera entre las filas. Me quote the manual:

currval

Return the value most recently obtained by nextval for this sequence in the current session. (An error is reported if nextval has never been called for this sequence in this session.) Because this is returning a session-local value, it gives a predictable answer whether or not other sessions have executed nextval since the current session did.

lastval

Return the value most recently returned by nextval in the current session. This function is identical to currval, except that instead of taking the sequence name as an argument it fetches the value of the last sequence used by nextval in the current session. It is an error to call lastval if nextval has not yet been called in the current session.

énfasis mío negrita.

Pero, como @Bernard commented, puede fallar después de todo.Pensándolo bien, esto tiene sentido: no hay garantía de que se llene el valor predeterminado (y nextval() llamado en el proceso) antes delastval() para llenar la segunda columna ltree. Así que quédate con la primera solución y nextval() para estar seguro.

+0

Exactamente lo que estaba buscando, ¡gracias! –

+0

¿Está garantizado que 'nextval()' se llamará primero para encontrar la siguiente clave primaria, de modo que 'lastval()' no arroje un error? – Bernard

+1

Su segunda sugerencia arroja un '55000 ERROR: lastval aún no está definido en esta sesión' en PostgreSQL 9.5. – Bernard

3

Esto funcionó en mi prueba:

INSERT INTO foo (id, ltree) VALUES (DEFAULT, (SELECT last_value from foo_id_seq)); 

Creo que hay una condición de carrera allí si dos insertos están sucediendo al mismo tiempo, ya que esto hace referencia al último valor de secuencia, en lugar de la fila actual. Yo personalmente estar más dispuestos a hacer esto (pseudo-código):

my $id = SELECT nextval('foo_id_seq'); 
INSERT INTO foo (id, ltree) VALUES ($id, '$id'); 
Cuestiones relacionadas