2009-02-08 6 views
9

Dans MS SQL Server, il est facile de créer des champs d'auto-incrémentation. Dans mes systèmes, j'ai arrêté d'utiliser des champs d'auto-incrémentation pour les clés primaires, et maintenant j'utilise Guid's. C'était génial, j'ai beaucoup d'avantages avec ce changement. Mais dans d'autres domaines clés non-primaires, j'avais vraiment besoin de mettre en place un "auto-incrémentation". C'est parce que mon système est indépendant de DB, donc je crée la valeur autoinc par programmation dans C#.Champs AutoIncrement sur des bases de données sans champ d'auto-incrémentation

Je voudrais des solutions pour des champs d'auto-incrémentation sur des bases de données sans auto-incrémentation, quelle est la solution que vous utilisez et pourquoi? Il y a une déclaration de Sql Ansi à ce sujet? et générer directement à partir de mon C#, est une meilleure solution?

PS: Je sais que select max (id) +1 de la table ce n'est pas vraiment concurrent amical ...

+0

Je suggérerais de réécrire le titre à quelque chose d'un peu plus clair comme "Créer des champs d'auto-incrémentation indépendants de la base de données". Mon souci est que je ne pense pas que je trouverais votre titre quand je ferais une recherche pour le même sujet. – Elijah

+0

Avez-vous vraiment besoin d'un nombre croissant d'incréments, ou avez-vous juste besoin d'un caractère unique? –

+0

Oui, j'ai besoin d'un "nombre continuellement croissant", pour d'autres situations j'utilise guids, CRC, etc ... –

Répondre

15

Le mécanisme permettant de générer des valeurs d'ID uniques ne doit pas être soumis à l'isolation des transactions. Cela est nécessaire pour que la base de données génère une valeur distincte pour chaque client, mieux que l'astuce SELECT MAX(id)+1 FROM table, ce qui entraîne une condition de concurrence si deux clients tentent d'allouer de nouvelles valeurs id simultanément.

Vous ne pouvez pas simuler cette opération à l'aide de requêtes SQL standard (sauf si vous utilisez des verrous de table ou des transactions sérialisables). Il doit s'agir d'un mécanisme intégré au moteur de base de données. ANSI SQL n'a pas décrit une opération pour générer des valeurs uniques pour les clés de substitution jusqu'à SQL: 2003. Avant cela, il n'y avait pas de norme pour les colonnes auto-incrémentées, donc presque chaque marque de SGBDR a fourni une solution propriétaire. Naturellement, ils varient beaucoup, et il n'y a aucun moyen de les utiliser d'une manière simple, indépendante de la base de données.

  • MySQL a l'option de colonne AUTO_INCREMENT, ou SERIAL pseudo-type de données qui est équivalent à BIGINT UNSIGNED AUTO_INCREMENT;
  • Microsoft SQL Server a l'option de colonne IDENTITY et NEWSEQUENTIALID() qui est quelque chose entre auto-incrémentation et GUID; Oracle a un objet SEQUENCE;
  • PostgreSQL a un objet SEQUENCE ou un pseudo-type de données SERIAL qui crée implicitement un objet de séquence conformément à une convention de dénomination;
  • InterBase/Firebird a un objet GENERATOR qui ressemble à un SEQUENCE dans Oracle; Firebird 2.1 prend également en charge SEQUENCE;
  • SQLite traite tout entier déclaré comme clé primaire comme auto-incrémenté implicitement;
  • DB2 UDB a à peu près tout: SEQUENCE objets, ou vous pouvez déclarer des colonnes avec l'option "GEN_ID".

Tous ces mécanismes fonctionnent en dehors de l'isolation des transactions, ce qui garantit que les clients simultanés obtiennent des valeurs uniques. Dans tous les cas, il est également possible d'interroger la dernière valeur générée pour votre session en cours. Il doit y avoir, de sorte que vous pouvez l'utiliser pour insérer des lignes dans une table enfant.

+1

dans Firebird 2.1: il y a aussi SEQUENCE http://www.firebirdsql.org/rlsnotesh/rlsnotes210.html#rnfb20x-ddl-syntax-create-sequence –

+0

@Hugues: Merci pour le conseil! Je l'ai ajouté à la liste. –

+0

SQL-Server a également le ** [NEWSEQUENTIALID()] (http://technet.microsoft.com/en-us/library/ms189786.aspx) ** qui est quelque chose entre auto-incrémentation et GUID. –

1

La plupart des bases de données qui ne sont pas des champs de autoincrement comme SQL Server (je pense Oracle spécifiquement) avoir des séquences où vous demandez à la séquence pour le numéro suivant. Peu importe combien de personnes demandent des numéros en même temps, tout le monde obtient un numéro unique.

1

La solution traditionnelle est d'avoir une table de ids qui ressemble à quelque chose comme ça

CREATE TABLE ids (
    tablename VARCHAR(32) NOT NULL PRIMARY KEY, 
    nextid INTEGER 
) 

qui s peuplé avec une ligne par table lors de la création de la base de données.

Vous effectuez ensuite une sélection pour obtenir l'ID suivant suivant pour la table que vous insérez, l'incrémenter et mettre à jour la table avec le nouvel ID. De toute évidence, il existe des problèmes de verrouillage, mais pour les bases de données avec des taux d'insertion modérés, cela fonctionne bien. Et c'est complètement portable.

+2

Cette solution est soumise à des conditions de concurrence, et chaque SGBDR majeur a une alternative de transaction-safe. –

2

Je pense que votre question est plutôt bonne. Cependant, il est facile de se perdre en essayant de trouver une solution SQL uniquement. En réalité, vous voudrez l'optimisation et la sécurité des transactions offertes par l'utilisation des implémentations de base de données des types auto-incrémentés.

Si vous devez extraire l'implémentation de l'opérateur d'auto-incrémentation, pourquoi ne pas créer une procédure stockée pour renvoyer votre valeur auto-incrémentée. La plupart des dialectes SQL accèdent aux procédures stockées de la même manière et devraient être plus portables. Vous pouvez ensuite créer une logique d'auto-incrémentation spécifique à la base de données lorsque vous créez le sproc, ce qui élimine le besoin de modifier plusieurs instructions pour qu'elles soient spécifiques au fournisseur.

Fait cette façon, vos inserts pourraient être aussi simple que:

INSERT INTO foo (id, name, rank, serial_number) 
VALUES (getNextFooId(), 'bar', 'fooRank', 123456); 

ensuite définir getNextFooId() dans une base de données de manière spécifique lorsque est en cours d'initialisation de la base de données.

0

Si vous avez besoin d'un champ d'auto-cryptage non-clé primaire, une très bonne solution MySQL pour créer des séquences d'arbitraires est d'utiliser la fonction last_insert_id(expr) relativement inconnue.

Si expr est donnée comme argument pour LAST_INSERT_ID(), la valeur de l'argument est renvoyé par la fonction et on se souvient que la prochaine valeur retournée par LAST_INSERT_ID(). Ce peut être utilisé pour simuler des séquences ...

(de http://dev.mysql.com/doc/refman/5.1/en/information-functions.html#function_last-insert-id)

Voici un exemple qui montre comment une séquence secondaire peut être conservé pour la numérotation des commentaires pour chaque poste:

CREATE TABLE `post` (
    `id` INT(10) UNSIGNED NOT NULL, 
    `title` VARCHAR(100) NOT NULL, 
    `comment_sequence` INT(10) UNSIGNED NOT NULL DEFAULT '0', 
    PRIMARY KEY (`id`) 
); 

CREATE TABLE `comment` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `post_id` INT(10) UNSIGNED NOT NULL, 
    `sequence` INT(10) UNSIGNED NOT NULL, 
    `content` TEXT NOT NULL, 
    PRIMARY KEY (`id`) 
); 

INSERT INTO post(id, title) VALUES(1, 'first post'); 
INSERT INTO post(id, title) VALUES(2, 'second post'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'blah'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'foo'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'bar'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'lorem'); 

UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2; 
INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'ipsum'); 

SELECT * FROM post; 
SELECT * FROM comment; 
Questions connexes