2009-05-26 8 views
2

En supposant que j'ai les tables suivantes.Comment créer un numéro de révision auto-incrémenté unique à une clé dans PGSQL?

PARENT: PARENT_ID serial, DESCRIPTION character varying(50) 

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

Ce que je voudrais voir est chaque ligne ENFANT ayant une child_id qui commence à 1 et par incréments de 1, unique par PARENT_ID. Ce serait semblable à un numéro de révision. Par exemple ..

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 

Est-il possible d'avoir la valeur child_id attribuée automatiquement, comme une séquence ou une contrainte, seule la possibilité de réutiliser une child_id qui a été supprimé? La seule façon que je peux comprendre est quelque chose à l'effet de ce SQL.

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

C'est un peu bidouillage. Je me rends compte que la normalisation de la base de données suggère que vous ne devriez pas avoir une clé liée à une autre, mais je n'ai pas cette option pour d'autres raisons. Des idées?

EDIT: Le titre est moche. Si l'un d'entre vous, les plus pointus, peut en trouver un qui soit plus précis, n'hésitez pas à le modifier.

Répondre

2

Je suggère d'utiliser:

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

Lorsque vous avez besoin pour obtenir un résultat souhaité:

  • Vous pouvez compter sur les lignes côté client.

  • Lors de la sélection des lignes où PARENT_ID =? vous pouvez utiliser une séquence temporaire.

  • En bientôt disponibles Postgresql 8.4 vous pouvez utiliser les fonctions de fenêtrage comme ceci:

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

Il est très rapide, facile à mettre en œuvre et intensifiera très bien car il n'y aura pas de problèmes de concurrence à se soucier.

+0

Je vous ai donné la réponse acceptée, merci pour la suggestion. J'avais complètement oublié à propos de row_number(). et le partitionnement est sympa. –

0

Ce insert n'est pas toute l'histoire cependant. Vous devrez également gérer les suppressions pour combler l'écart créé si vous voulez vraiment que les numéros soient contigus.

Ma suggestion serait de déduire cette valeur comme vous en avez besoin. Qu'est-ce qui détermine l'ordre du nombre? Si c'est la date entrée dans le système, ajoutez cette date à votre table et placez votre PK sur le parent_id et cette date, alors vous pouvez facilement trouver le numéro par SQL ou dans le frontal comme vous en avez besoin.

0

Vous pouvez utiliser un numéro de version incrémenter sur la table parent et définir l'identifiant de l'enfant à cette valeur et l'incrémenter. Vous devrez probablement mettre à jour la ligne parente et insérer la ligne enfant dans une seule transaction. Ceci permettra d'assurer que les identifiants enfants sont strictement ascendants, mais ne réutiliseront pas les valeurs d'id après une suppression. Si vous pouvez éviter la contrainte de réutiliser les anciens identifiants, cela simplifiera votre solution (et la solution sera plus efficace). Si vous devez réutilisation ids alors vous avez 2 options, tout d'abord la solution que vous avez spécifié ci-dessus, mais sur la suppression renumérotation toutes les valeurs qui se produisent après celui que vous avez supprimé. L'autre option est d'avoir une sorte de fonction qui analyse les identifiants enfants dans l'ordre et les compare à un ensemble de nombres séquentiels et renvoie la valeur lorsque le premier n'est pas trouvé. Ces deux solutions sont plus complexes et plus lentes, car vous devrez retirer un verrou de ligne pour empêcher les mises à jour simultanées et les insertions ou les insertions et les suppressions entraîneront une pénalité O (n).

Questions connexes