2009-02-08 9 views
9

In MS Sql Server è facile creare campi autoincrement. Nei miei sistemi ho smesso di utilizzare i campi di autoincremento per le chiavi primarie e ora utilizzo Guid's. È stato fantastico, ho molti vantaggi con quel cambiamento. Ma in altri campi chiave non primari, avevo davvero bisogno di implementare un "autoincremento morbido". È perché il mio sistema è indipendente dal DB, quindi creo il valore autoinc programmaticamente in C#.Campi AutoIncrement su database senza campo autoincrement

Desidero soluzioni per i campi di autoincremento su database senza autoincremento, quale soluzione si utilizza e perché? C'è qualche dichiarazione Sql Ansi su questo? e generando direttamente dal mio C#, è una soluzione migliore?

PS: so che select max (id) +1 dalla tabella non è davvero concomitante amichevole ...

+0

Suggerirei di riscrivere il titolo in qualcosa che sia un po 'più chiaro come "Creazione di campi di autoincremento indipendenti dal database". La mia preoccupazione è che non penso che troverei il tuo titolo quando farei una ricerca per lo stesso argomento. – Elijah

+0

Hai davvero bisogno di un numero in continuo aumento, o hai solo bisogno di unicità? –

+0

Sì, ho bisogno di "un numero a incremento continuo", per altre situazioni utilizzo guids, CRC, ecc ... –

risposta

15

Il meccanismo per generare valori ID univoci non deve essere soggetto all'isolamento della transazione. Ciò è necessario affinché il database generi un valore distinto per ciascun client, migliore del trucco di SELECT MAX(id)+1 FROM table, che si traduce in una condizione di competizione se due client tentano di allocare contemporaneamente nuovi valori id.

Non è possibile simulare questa operazione utilizzando query SQL standard (a meno che non si utilizzino blocchi tabella o transazioni serializzabili). Deve essere un meccanismo integrato nel motore del database.

ANSI SQL non ha descritto un'operazione per generare valori univoci per chiavi surrogate fino a SQL: 2003. Prima di questo, non esisteva uno standard per le colonne di auto-incremento, quindi quasi ogni marchio di RDBMS forniva una soluzione proprietaria. Naturalmente variano molto, e non c'è modo di usarli in un modo semplice, indipendente dal database.

  • MySQL ha la possibilità AUTO_INCREMENT colonna o SERIAL pseudo-tipo di dati che è equivalente a BIGINT UNSIGNED AUTO_INCREMENT;
  • Microsoft SQL Server ha l'opzione di colonna IDENTITY e NEWSEQUENTIALID() che è qualcosa tra l'incremento automatico e il GUID;
  • Oracle ha un oggetto SEQUENCE;
  • PostgreSQL ha un oggetto SEQUENCE o uno pseudo-datatype SERIAL che crea in modo implicito un oggetto sequenza in base a una convenzione di denominazione;
  • InterBase/Firebird ha un oggetto GENERATOR che è un po 'come un SEQUENCE in Oracle; Firebird 2.1 supporta anche SEQUENCE;
  • SQLite tratta qualsiasi intero dichiarato come chiave primaria come auto-incremento implicito;
  • DB2 UDB ha praticamente tutto: SEQUENCE oggetti oppure è possibile dichiarare colonne con l'opzione "GEN_ID".

Tutti questi meccanismi operano al di fuori dell'isolamento della transazione, garantendo che i client concorrenti ottengano valori univoci. Inoltre, in tutti i casi esiste un modo per interrogare il valore generato più recentemente per la sessione corrente. Ci deve essere, quindi puoi usarlo per inserire righe in una tabella figlia.

+1

in Firebird 2.1: c'è anche SEQUENCE http://www.firebirdsql.org/rlsnotesh/rlsnotes210.html#rnfb20x-ddl-syntax-create-sequence –

+0

@Hugues: Grazie per il suggerimento! L'ho aggiunto alla lista. –

+0

SQL-Server ha anche il ** [NEWSEQUENTIALID()] (http://technet.microsoft.com/en-us/library/ms189786.aspx) ** che è qualcosa tra l'auto-incremento e il GUID. –

1

maggior parte dei database che non dispongono di campi autoincremento come SQL Server (sto pensando di Oracle nello specifico) hanno sequenze in cui chiedi alla sequenza il numero successivo. Non importa quante persone richiedano numeri allo stesso tempo, ognuno ottiene un numero univoco.

1

La soluzione tradizionale è quella di avere una tabella di ID che sembrano qualcosa di simile

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

che s popolata con una riga per ogni tabella quando si crea il database.

Quindi si esegue una selezione per ottenere il prossimo id successivo per la tabella in cui si sta inserendo, incrementarlo e quindi aggiornare la tabella con il nuovo id. Ovviamente, ci sono problemi di blocco qui, ma per i database con frequenze di inserimento moderate funziona bene. Ed è completamente portatile.

+2

Questa soluzione è soggetta a condizioni di gara e ogni RDBMS principale ha un'alternativa sicura per le transazioni. –

2

Penso che la tua domanda sia davvero buona. Tuttavia, è facile perdersi cercando di trovare una soluzione solo SQL. In realtà vorrai l'ottimizzazione e la sicurezza delle transazioni offerte utilizzando le implementazioni del database dei tipi di autoincrement.

Se è necessario astrarre l'implementazione dell'operatore di autoincremento, perché non creare una stored procedure per restituire il valore di incremento automatico. La maggior parte dei dialetti SQL accede alle stored procedure in modo relativamente simile e dovrebbe essere più portabile. Quindi è possibile creare una logica di autoincremento specifica del database quando si crea lo sproc, eliminando la necessità di modificare molte istruzioni per essere specifiche del fornitore.

fatto in questo modo, i vostri inserti potrebbe essere semplice come:

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

Definire quindi getNextFooId() in un database modo specifico quando il database viene inizializzato.

0

Se è necessario un campo di autoincrement non primario, una soluzione MySQL molto utile per la creazione di sequenze arbitrarie è l'uso della funzione relativamente sconosciuta last_insert_id(expr).

Se espressione è data come argomento LAST_INSERT_ID(), il valore dell'argomento viene restituito dalla funzione ed è ricordato come il valore accanto a essere restituito da LAST_INSERT_ID(). Questo può essere utilizzato per simulare le sequenze ...

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

Ecco un esempio che dimostra come una sequenza secondaria può essere mantenuto per la numerazione commenti per ogni post:

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;