2012-09-14 4 views
8

Ich habe eine Tabelle mit einem Primärschlüssel und auch eine ltree Spalte, deren Wert ich die Verkettung dieser Primärschlüssel sein möchte. z.B.Referenzwert der seriellen Spalte in einer anderen Spalte während der gleichen INSERT

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

Ich bin neugierig, wenn es eine Möglichkeit gibt, eine solche Einfügung in einer Abfrage, z.

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

ich wahrscheinlich bin Übervorteilung hier und versuchen, in einer Abfrage tun, was soll ich in zwei tun (in einer Transaktion gruppiert).

+0

Dieser Pfad sieht nicht sehr abfragbar .. –

+0

Meine Pfadwerte basieren auf diesem: http://StackOverflow.com/a/607379/39529 –

+0

PostgreSQL hat Rekursion, gemeinsame Tabellenausdruck, viel einfacher und schneller als Ihre Pfadlösung/Workaround. –

Antwort

7

Sie eine Unterabfrage oder eine beschreibbare CTE verwenden könnte den Wert aus der Sequenz einmal abrufen und wiederholt verwenden:

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

Data-modifying CTE erfordert Postgres 9.1 oder höher.

Wenn Sie nicht sicher über den Namen der Sequenz sind, können Sie pg_get_serial_sequence() statt:

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

Wenn der Tabellenname „foo“ möglicherweise nicht in allen Schema in der DB eindeutig sein, können Sie Schema -qualifizieren Sie es. Und wenn die Schreibweise eines Namens nicht-Standard ist, müssen Sie doppelte Anführungszeichen:

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



Schnelltests zeigten @Mark's idea mit lastval()konnte Arbeit zu:

INSERT INTO foo (ltree) VALUES ('1.' || lastval()); 
  • Sie können id einfach aus der Abfrage herauslassen, die Spalte serial wird automatisch zugewiesen. Macht keinen Unterschied.

  • Es sollte keine Wettlaufsituation zwischen den Reihen geben. Ich 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 Hervorhebung von mir.

Aber, wie @Bernard commented, kann es doch scheitern.Auf den zweiten Gedanken macht dies Sinn: Es gibt keine Garantie, dass der Standardwert gefüllt ist (und nextval() im Prozess aufgerufen) vorlastval() wird aufgerufen, um die zweite Spalte ltree zu füllen. Also bleib bei der ersten Lösung und nextval() um sicher zu gehen.

+0

Genau das, was ich gesucht habe, danke! –

+0

Ist garantiert, dass 'nextval()' zuerst aufgerufen wird, um den nächsten Primärschlüssel zu finden, so dass 'lastval()' keinen Fehler liefert? – Bernard

+1

Ihr zweiter Vorschlag löst einen '55000 FEHLER aus: lastval ist in dieser Sitzung noch nicht definiert' auf PostgreSQL 9.5. – Bernard

3

Das funktionierte in meinem Test:

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

Ich denke, eine Race-Bedingung gibt es, wenn zwei Einsätze zur gleichen Zeit geschehen, da dies den letzten Sequenzwert verweist, anstatt die aktuellen Zeile. Ich persönlich würde eher geneigt sein, diese (Pseudocode) zu tun:

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