2

次のテーブルがあると仮定します。

PARENT: PARENT_ID serial, DESCRIPTION character varying(50)

CHILD: PARENT_ID integer, CHILD_ID integer, DESCRIPTION character varying(50)

私が見たいのは、PARENT_IDごとに一意で、1から始まり1ずつ増加するCHILD_IDを持つCHILDの各行です。リビジョン番号に似ています。例えば..

PARENT_ID 1, CHILD_ID 1
PARENT_ID 1, CHILD_ID 2
PARENT_ID 1, CHILD_ID 3
PARENT_ID 2, CHILD_ID 1
PARENT_ID 3, CHILD_ID 1
PARENT_ID 3, CHILD_ID 2

削除された CHILD_ID を再利用する機能のみを使用して、シーケンスや制約など、CHILD_ID 値を自動的に割り当てる方法はありますか? 私が理解できる唯一の方法は、この SQL の効果に対するものです。

INSERT INTO child SELECT parent_id, MAX(child_id)+1, 'description' FROM child WHERE parent_id = :PARENT_ID GROUP BY parent_id

しかし、それはちょっとしたハックです。データベースの正規化では、あるキーを別のキーに関連付けるべきではないことが示唆されていますが、他の理由でそのオプションはありません。何か案は?

編集:タイトルは醜いです。スコアの高い方で、より正確な方法を思いついた場合は、お気軽に変更してください。

4

3 に答える 3

4

私は使用することをお勧めします:

CHILD: PARENT_ID integer, CHILD_ID serial, DESCRIPTION character varying(50)

望ましい結果を得る必要がある場合:

  • クライアント側で行を数えることができます。

  • PARENT_ID=? の行を選択する場合 一時的なシーケンスを使用できます。

  • 間もなくリリースされる Postgresql 8.4 では、次のようなウィンドウ関数を使用できます。

    $ create table child (parent_id integer, child_id serial);
    NOTICE:  CREATE TABLE will create implicit sequence "child_child_id_seq" for serial column "child.child_id"
    CREATE TABLE
    
    $ insert into child (parent_id) values (1), (1), (1), (2), (3), (3);
    
    $ select * from child;
     parent_id | child_id 
    -----------+----------
             1 |        1
             1 |        2
             1 |        3
             2 |        4
             3 |        5
             3 |        6
    (6 rows)
    
    $ select parent_id, row_number() over (partition by parent_id order by child_id) from child;
     parent_id | row_number 
    -----------+------
             1 |          1
             1 |          2
             1 |          3
             2 |          1
             3 |          1
             3 |          2
    (6 rows)
    

これは非常に高速で、実装が簡単で、並行性の問題を心配する必要がないため、非常にうまく拡張できます。

于 2009-05-27T08:35:02.383 に答える
0

ただし、その挿入はすべてではありません。数字を本当に連続させたい場合は、作成されたギャップを埋めるために削除を処理する必要もあります。

私の提案は、必要に応じてこの値を導出することです。数字の順番は何によって決まる?それがシステムに入力された日付である場合は、その日付をテーブルに追加し、PK をparent_id とその日付の上に配置すると、必要に応じて SQL またはフロント エンドで簡単に番号を取得できます。

于 2009-05-26T19:46:45.257 に答える
0

親テーブルで増分バージョン番号を使用し、子 ID をその値に設定して増分することができます。おそらく、親行を更新し、子行を単​​一のトランザクションで挿入する必要があります。

BEGIN
-- Get and hold onto parent_id and version values.
SELECT PARENT_ID, VERSION FROM PARENT WHERE PARENT_ID = :PARENT_ID;
-- Use the values to insert into the child table
INSERT INTO CHILD (PARENT_ID, CHILD_ID) VALUES (:PARENT_ID, :VERSION);
-- Update the version using an optimistic lock.
UPDATE PARENT SET VERSION = VERSION + 1 WHERE PARENT_ID = :PARENT_ID AND 
                                              VERSION = :VERSION_ID
-- If no rows are updated rollback the transaction and try again.
END

これにより、子 ID は厳密に昇順になりますが、削除後に ID 値を再利用することはありません。古い ID を再利用するという制約を回避できれば、ソリューションが簡素化されます (ソリューションはより効率的になります)。IDを再利用する必要がある場合は、2 つのオプションがあります。最初は上記で指定した解決策ですが、削除時に、削除した値の後に発生するすべての値の番号を付け直します。もう 1 つのオプションは、子 ID を順番にスキャンし、一連の一連の番号と比較して、最初の ID が見つからない場合に値を返す何らかの関数を用意することです。これらのソリューションは両方ともより複雑であり、同時更新を防ぐために行ロックを取得する必要があり、挿入または挿入と削除の両方で O(n) ペナルティが発生するため、遅くなります。

于 2009-05-26T20:24:20.377 に答える