2016-08-23 7 views
0

私は以下の表をしました:PostgreSQLの自己参照表 - 親IDをスクリプトに格納する方法は?

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); 

は今、私はデータを挿入する必要があります。

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

そして今、私はそれに子供を追加するだけで挿入された親のIDが必要になります:だから私は親で始まる

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>); 

私が手にして、後に親のIDを格納することができますどのようにそれ以降のインサートで使用しますか?

+0

@a_horse_with_no_nameとともにRETURNINGを使用することであるが、それはこのように動作しません。両方の子供のために、私は 'PARENT_ID'の同じ値を必要とします、両方の関数は私に最新のものを与えます。 – Opal

答えて

0

答えは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

@a_horse_with_no_nameの答えをコピーする必要はありません。削除してください... – joop

+0

@joop、これは私自身の答えです、私は同時にそれを追加していて、自分でそれを働かせました。 – Opal

-1

tl;dr:後藤オプション3:RETURNINGとINSERT)PostgreSQLのテーブルには、「ID」をコンセプトに、一般的に必ずしも必要ではないが、代理のデフォルト値として使用されているだけでシーケンス(存在しないこと

リコールプライマリキー、SERIAL疑似タイプ)。


オプション1:CURRVAL(<sequence name>);あなたが新たに挿入された行のIDを得ることに興味を持っている場合は

は、いくつかの方法があります。例えば

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

シーケンスの名前はそれが本当に任意のだ、知らなければなりません。この例では、表personsには疑似型で作成されたid列があると仮定します。これに頼らないようにするには、よりクリーンな感じに、あなたの代わりにpg_get_serial_sequenceを使用することができます。

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

警告:currval()のみ(nextval()を実行した)INSERT後に動作し、同じセッションで


オプション2:LASTVAL();

これは、シーケンス番号を指定する必要はありませんだけで、以前のようになります。それは同じ、常に自分のセッション内(直近の修飾された配列を探します上記のように注意してください)。


両方CURRVALLASTVALは完全に同時安全です。 PGのシーケンスの動作は、異なるセッションが干渉しないように設計されているため、競合状態のリスクはありません(別のセッションがINSERTとSELECTの間に別の行を挿入しても正しい値が得られます)。

これらは微妙な潜在的な問題があります。データベースにテーブルに挿入すると、TRIGGER(またはRULE)があると、他のテーブルに余分な挿入が行われます... LASTVALはおそらく間違った値を与えます。余分な挿入が同じpersonsテーブルに行われた場合、この問題はCURRVALで発生する可能性があります(これはずっと一般的ですが、リスクは依然として存在します)。


オプション3:INSERTRETURNING

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

とこれがIDを取得するための最も、クリーン効率的かつ安全な方法です。以前のリスクはありません。

欠点?ほとんどの場合、INSERT文を呼び出す方法を変更する必要があります(最悪の場合、APIまたはDB層でINSERTが値を返すとは思われません)。それは標準的なSQL(誰が気にしますか)ではありません。あなたは、他の場所でオプション3のために行くことができた場合は、1

注意を好む:それは、PostgreSQL 8.2(2006年12月...)


結論以降で利用可能ですあなたがする場合は、すべてのこれらのメソッドは役に立ちません最後にグローバルに挿入されたIDを取得してください(必ずしもあなたのセッションではありません)。そのためにはselect max(id) from tableにする必要があります(もちろん、他のトランザクションからコミットされていない挿入は読み取られません)。

+0

プレーンなSQLスクリプトでオプション3を使用することはできません –

+0

オプション3で返されたIDはどのように格納し、後続の挿入で使用できますか? – Opal

+0

'currval'と' lastval'の両方が動作しないようです。 2番目の最初の子が挿入された後にシーケンスの値が変更され、親IDの値が失われます。 – Opal

2

あなたはreturning句でCTEを修正するデータを使用することができます。

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

ありがとう、それは私が自分で取り組んだことです! – Opal

関連する問題