2016-08-23 1 views
0

Ich habe die folgende Tabelle:PostgreSQL selbstreferentielle Tabelle - wie Eltern ID im Skript zu speichern?

DROP SEQUENCE IF EXISTS CATEGORY_SEQ CASCADE; 
CREATE SEQUENCE CATEGORY_SEQ START 1; 

DROP TABLE IF EXISTS CATEGORY CASCADE; 

CREATE TABLE CATEGORY (
    ID  BIGINT     NOT NULL DEFAULT nextval('CATEGORY_SEQ'), 
    NAME  CHARACTER VARYING(255) NOT NULL, 
    PARENT_ID BIGINT 
); 

ALTER TABLE CATEGORY 
    ADD CONSTRAINT CATEGORY_PK PRIMARY KEY (ID); 
ALTER TABLE CATEGORY 
    ADD CONSTRAINT CATEGORY_SELF_FK FOREIGN KEY (PARENT_ID) REFERENCES CATEGORY (ID); 

Jetzt muss ich die Daten einfügen. So beginne ich mit den Eltern:

INSERT INTO CATEGORY (NAME) VALUES ('PARENT_1'); 

Und jetzt brauche ich die ID des gerade eingefügt Eltern Kinder, um es hinzuzufügen:

INSERT INTO CATEGORY (NAME, PARENT_ID) VALUES ('CHILDREN_1_1', <what_goes_here>); 
INSERT INTO CATEGORY (NAME, PARENT_ID) VALUES ('CHILDREN_1_2', <what_goes_here>); 

Wie kann ich und speichern Sie die ID des übergeordneten später Verwenden Sie es in den nachfolgenden Einsätzen?

+0

@a_horse_with_no_name zu verwenden, wird es auf diese Weise nicht funktionieren. Für beide Kinder benötige ich den gleichen Wert von 'PARENT_ID', beide Funktionen geben mir das Neueste. – Opal

Antwort

0

Die Antwort ist RETURNING zusammen mit WITH

WITH inserted AS (
    INSERT INTO CATEGORY (NAME) VALUES ('PARENT_1') 
    RETURNING id 
) INSERT INTO CATEGORY (NAME, PARENT_ID) VALUES 
    ('CHILD_1_1', (SELECT inserted.id FROM inserted)), 
    ('CHILD_2_1', (SELECT inserted.id FROM inserted)); 
+0

Sie müssen die Antwort von @a_horse_with_no_name nicht kopieren. Bitte entfernen ... – joop

+0

@joop, das ist meine eigene Antwort, ich habe es zur selben Zeit hinzugefügt und es selbst ausgearbeitet. – Opal

-1

(tl;dr: goto Option 3: INSERT mit RÜCKKEHR)

Daran erinnern, dass in postgresql es keine „id“ -Konzept ist für Tabellen, nur Sequenzen (die in der Regel, aber nicht notwendigerweise als Standardwerte für Surrogat verwendet Primärschlüssel mit dem Pseudotyp SERIAL).

Wenn Sie sich für die ID eines neu eingefügten Zeile interessiert sind, gibt es mehrere Möglichkeiten:


Option 1: CURRVAL(<sequence name>);.

Zum Beispiel:

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John'); 
    SELECT currval('persons_id_seq'); 

Der Name der Sequenz bekannt sein müssen, es ist wirklich willkürlich; In diesem Beispiel nehmen wir an, dass die Tabelle persons eine id-Spalte hat, die mit dem Pseudotyp SERIAL erstellt wurde. Um zu vermeiden, auf diese zu verlassen und mehr sauber zu fühlen, können Sie stattdessen pg_get_serial_sequence verwenden:

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John'); 
    SELECT currval(pg_get_serial_sequence('persons','id')); 

Caveat: currval() funktioniert nur nach einem INSERT (die nextval() hat ausgeführt), in der gleichen Sitzung.


Option 2: LASTVAL();

Dies ist ähnlich dem vorherigen, nur dass Sie die Sequenznummer nicht angeben müssen: es für das letzte modifizierte Sequenz sieht (immer in Ihrer Sitzung, gleiche Einschränkung wie oben).


Beide CURRVAL und LASTVAL sind völlig gleichzeitig sicher. Das Verhalten der Sequenz in PG ist so ausgelegt, dass unterschiedliche Sessions nicht stören, also besteht kein Risiko von Race Conditions (wenn eine andere Session eine andere Zeile zwischen meinem INSERT und meinem SELECT einfügt, bekomme ich immer noch meinen korrekten Wert).

Allerdings sie haben ein subtiles potenzielles Problem. Wenn die Datenbank einige TRIGGER (oder RULE) hat, die beim Einfügen in persons Tabelle, einige zusätzliche Einfügungen in anderen Tabellen macht ... dann LASTVAL wird uns wahrscheinlich den falschen Wert geben.Das Problem kann sogar passieren mit CURRVAL, wenn die zusätzlichen Einfügungen in die gleiche Tabelle persons getan werden (das ist viel weniger üblich, aber das Risiko besteht immer noch).


Option 3: INSERT mit RETURNING

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id; 

Dies ist der saubere, effizienter und sicherer Weg, um die ID zu bekommen. Es hat keine der Risiken des vorherigen.

Nachteile? Fast keine: Möglicherweise müssen Sie die Art ändern, wie Sie Ihre INSERT-Anweisung aufrufen (im schlimmsten Fall erwartet Ihre API- oder DB-Schicht möglicherweise nicht, dass ein INSERT einen Wert zurückgibt). es ist kein Standard SQL (wen interessiert das); es ist verfügbar seit Postgresql 8.2 (Dezember 2006 ...)


Fazit: Wenn Sie, 3. In anderen Ländern für die Option gehen, bevorzugt 1.

Hinweis: All diese Methoden sind nutzlos, wenn Sie beabsichtigen, get die letzte global eingefügte ID (nicht unbedingt in Ihrer Sitzung). Dazu müssen Sie auf select max(id) from table zurückgreifen (natürlich werden nicht festgeschriebene Einfügungen aus anderen Transaktionen nicht gelesen).

+0

Option 3 kann nicht in einem einfachen SQL-Skript verwendet werden –

+0

Wie kann ich ID speichern, die in Option 3 zurückgegeben wurde, und sie in nachfolgenden Einfügungen verwenden? – Opal

+0

Es scheint auch, dass sowohl 'currval' und' lastval' nicht funktionieren. Der Wert der Sequenz wird geändert, nachdem das zweite erste Kind eingefügt wurde, daher geht der Wert der Eltern-ID verloren. – Opal

2

Sie ein Datum modifiziert CTE mit der returning Klausel verwenden:

with parent_cat (parent_id) as (
    INSERT INTO CATEGORY (NAME) VALUES ('PARENT_1') 
    returning id 
) 
INSERT INTO CATEGORY (NAME, PARENT_ID) 
VALUES 
    ('CHILDREN_1_1', (select parent_id from parent_cat)), 
    ('CHILDREN_1_2', (select parent_id from parent_cat)); 
+0

Danke, das habe ich selbst erarbeitet! – Opal

Verwandte Themen