2012-09-14 11 views
8

Ho una tabella con una chiave primaria SERIAL e anche una colonna ltree, il cui valore Voglio essere la concatenazione di quelle chiavi primarie. per esempio.Valore di riferimento della colonna seriale in un'altra colonna durante lo stesso INSERT

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

Sono curioso di sapere se c'è un modo per fare un tale inserto in una query, ad esempio,

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

Sono probabilmente overreaching qui e cercando di fare in una query che cosa dovrei fare in due (raggruppate in una transazione).

+0

Quella strada non sembra molto queryable .. –

+0

miei valori del percorso si basano su questo: http://stackoverflow.com/a/607379/39529 –

+0

PostgreSQL ha ricorsione, espressione di tabella comune, molto più facile e più veloce di la soluzione del tuo percorso/soluzione alternativa. –

risposta

7

Si potrebbe utilizzare una sottoquery o un CTE scrivibile per recuperare il valore dalla sequenza volta e utilizzarlo ripetutamente:

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

Data-modifying CTE richiede Postgres 9.1 o successivo.

Se non si è sicuri circa il nome della sequenza è possibile utilizzare pg_get_serial_sequence() invece:

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

Se il nome della tabella "foo" potrebbe non essere univoco in tutto lo schema nel DB, è possibile schema -equalizzarlo E se l'ortografia di qualsiasi nome è non standard, è necessario fare doppio citazione:

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



test rapidi indicati @Mark's idea con lastval()poteva lavoro troppo:

INSERT INTO foo (ltree) VALUES ('1.' || lastval()); 
  • Puoi lasciare id fuori dalla query, la colonna serial verrà assegnata automaticamente. Non fa differenza.

  • Non dovrebbe esserci una condizione di competizione tra le file. I 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.

Bold sottolineatura mia.

Ma, come @Bernard commented, può fallire dopo tutto.A pensarci bene, questo ha senso: non vi è alcuna garanzia che il valore predefinito sia riempito (e nextval() chiamato nel processo) prima dellastval() viene chiamato per riempire la 2a colonna ltree. Quindi attenersi alla prima soluzione e nextval() per essere sicuro.

+0

Esattamente quello che stavo cercando, grazie! –

+0

È garantito che 'nextval()' sarà chiamato prima per trovare la prossima chiave primaria, in modo che 'lastval()' non genererà un errore? – Bernard

+1

Il tuo secondo suggerimento lancia un '55000 ERRORE: lastval non è ancora definito in questa sessione' su PostgreSQL 9.5. – Bernard

3

questo ha funzionato nel mio test:

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

Penso che ci sia una condizione di competizione lì se due inserti stanno accadendo allo stesso tempo, dal momento che questo fa riferimento l'ultimo valore della sequenza, invece della riga corrente. Io personalmente essere più inclini a fare questo (pseudo-codice):

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