6
votes

Comment créer plusieurs séquences dans une table?

J'ai une table "reçus". J'ai des colonnes client_id (qui avait reçu le reçu) et la réception. Le recettement_number doit démarrer sur 1 pour chaque client et être une séquence. Cela signifie que Customer_id et la réception_number seront uniques. Comment puis-je faire ceci élégamment? Puis-je utiliser la fonctionnalité de séquestration intégrée avec une séquence de création ou similaire? On dirait que je devrais créer une séquence pour chaque client, ce qui n'est bien sûr pas une solution élégante.

Edit: Il doit y avoir un moyen sûr de faire du thread-coffre-fort et de l'idiot pour le faire. Il devrait être un besoin assez simple / courant.


4 commentaires

Avez-vous vraiment besoin de stocker cela? Ces numéros uniques peuvent facilement être générés lors de la récupération des données.


@a_horse_with_no_name Oui, cela doit être auditable.


Je ne suis pas sûr de ce que vous entendez par "thread-coffre". Vous devez utiliser des transactions pour faire une séquence d'actions "Atomic". Les transactions atomiques qui sont des actions complètes devraient être sur le thread-coffre-fort à moins que je ne comprends pas mal compris.


@Waltermitty vous avez raison. C'est un moyen de résoudre ce problème. Ce que je n'aime pas sur l'emballage des choses dans une transaction, c'est que les programmeurs novices ne comprennent parfois pas l'importance des transactions, alors je les considère plus risqués que de manière intégrée.


5 Réponses :


1
votes

Entrez la description de l'image ici

-- next CustomerReceiptNo
select coalesce(max(CustomerReceiptNo), 0) + 1
from  Receipt
where CustomerId = specific_customer_id;


0 commentaires

1
votes

Pourquoi les numéros de réception commencent-ils à 1 pour chaque client? Est-ce cette partie des exigences définies?

Le moyen le plus simple de faire ce fait est d'avoir le programme qui génère de nouvelles recettes interroge la base de données pour max (reçu de réception) où la clientigité = actuelCustoméride puis ajoutez 1.

CurrentCustomérid est une variable de programme non une valeur de base de données.

Ceci est un peu inélégant qui implique une recherche supplémentaire de la table. Vous devrez créer soigneusement vos index afin d'obtenir l'un des index pour répondre à la question sans balayage de la table complète.

Une alternative qui est un peu plus rapide au moment de l'insertion consiste à créer une colonne supplémentaire, appelée maxreeceIPTnumber, dans la table des clients. Incrément que lorsque vous voulez insérer un nouveau reçu.


1 commentaires

Je suis inquiet que cela prendrait soin de pouvoir être en sécurité



1
votes

Vous pouvez utiliser un déclencheur comme celui-ci pour mettre à jour votre colonne:

Définition de la table avec une contrainte unique sur Customer_Id, Reception_Number: P>

  id | customer_id | receipt_number 
 ----+-------------+----------------  
  14 |           1 |              1  
  15 |           1 |              2  
  16 |           2 |              1 
  17 |           2 |              2  
  18 |           2 |              3


4 commentaires

Cela produira des résultats incorrects si plusieurs transactions sont insérées dans la table RECEITIPTES .


C'est vrai, mais je pense que vous devriez pouvoir faire en sorte que l'opération soit en sécurité en verrouillant le tableau au début de la fonction.


Comme A_HORSE_WITH_NO_NAME mentionné, je suis également inquiet que cela s'occupe de la sécurité. Il doit y avoir une meilleure façon?


@David, j'ai fait une légère modification du code pour vous qui verrouille la table jusqu'à ce que la transaction d'insertion soit terminée. Vous pouvez tester cela en commençant une transaction en deux sessions différentes et tenter d'insérer une valeur dans les deux (on bloquera jusqu'à ce que l'autre soit commise). Si dans des conditions normales, vos transactions se produisent rapidement - cela ne devrait pas être un problème, mais si les choses fonctionnent longtemps, vous verrez le blocage. Vous voudrez peut-être aussi simplement envisager une table séparée que vous pouvez mettre à jour / sélectionner à partir de celui contient les paires de la société / des reçus et utilisez-la pour stocker votre quasi-séquence.



3
votes

Séquence ne garantit pas qu'il n'y a pas de lacunes. Par exemple, une transaction peut générer un nouveau numéro puis abandonner (en raison d'un bogue ou d'une panne de courant ou autre ...). La prochaine transaction obtiendrait alors aveuglément le numéro suivant, pas celui qui était "perdu".

Il serait préférable que votre demande de client ne dépendra pas de l'hypothèse de "sans lacunes" dans la place des sapins. Cependant, vous pouvez minimiser les lacunes comme ceci:

  1. Sélectionnez max (réception_Number) à partir de reçus où Customer_ID =: CI
  2. Insérer dans les reçus (Customer_id, Numéro de réception) Valeurs (: CI, Abveresult + 1) ou simplement insérer 1 si Étape 1 est retourné Null.
  3. Si étape 2 a retourné une violation PK * , réessayez depuis le début.

    * car une transaction simultanée a traversé le même processus et engagé.

    tant que des lignes sont encore ajoutées et Non supprimé, cela devrait empêcher les lacunes, même dans un environnement simultané.


    BTW, vous pouvez "condenser" étapes 1 et 2 Comme ceci: xxx

    [SQL FIDDLE] < / a>

    L'index sous la pc {Customer_ID, la réception_Number} doit s'assurer que la partie sélectionnée de cette requête est satisfaite efficacement.


0 commentaires

0
votes

Je voudrais proposer ma solution à ce problème - utilisez +1 colonne de la table du client pour stocker la dernière page_recetiet_id et utiliser la fonction incrémentielle Next_receipt_id (client_id): xxx

alors vous pouvez soit soit Utilisez-le dans la gâchette d'insertion de réception: xxx

ou à l'intérieur de votre ormes (pseudocode): xxx

ignorance de toute concurrence Sur inserts, vous aurez toujours des identifiants séquentiels.

acclamations!


0 commentaires