2009-03-19 16 views
14

In Oracle esiste un meccanismo per generare numeri di sequenza, ad es.Sequenza Oracle, ma in MS SQL Server

CREATE SEQUENCE supplier_seq 

    MINVALUE 1 
    MAXVALUE 999999999999999999999999999 
    START WITH 1 
    INCREMENT BY 1 
    CACHE 20; 

e quindi eseguire la dichiarazione

supplier_seq.nextval 

per recuperare il numero di sequenza successiva.

Come creeresti la stessa funzionalità in MS SQL Server?

Modifica: non sto cercando modi per generare automaticamente le chiavi per i record di tabella. Devo generare un valore univoco che possa essere utilizzato come ID (logico) per un processo. Quindi ho bisogno delle funzionalità esatte fornite da Oracle.

risposta

15

Non c'è una corrispondenza esatta.

L'equivalente è IDENTITY che è possibile impostare come tipo di dati durante la creazione di una tabella. SQLSERVER creerà automaticamente un numero di sequenza in esecuzione durante l'inserimento. L'ultimo valore inserito può essere ottenuto chiamando SCOPE_IDENTITY() o consultando la variabile di sistema @@ IDENTITY (come indicato da Frans)

Se è necessario l'equivalente esatto, è necessario creare una tabella e quindi scrivere una procedura per riattivare il valore successivo e altre operazioni. Vedi la risposta di Marks sulle insidie ​​su questo.

Modifica:
SQL Server ha implementato la sequenza simile a Oracle. Si prega di fare riferimento a questa domanda per maggiori dettagli.

How would you implement sequences in Microsoft SQL Server?

+0

Ho bisogno l'esatto equivalente, e l'uso di una procedura è stata la prima idea pure. Mi stavo chiedendo se ci fosse una soluzione più "naturale" a questo. – Raymond

+0

Sei sfortunato qui allora. – Dheer

+1

Sembra che SQL Server 2012 abbia introdotto gli oggetti SEQUENCE per http://stackoverflow.com/questions/282943/how-would-you-implement-sequences-in-microsoft-sql-server e http://msdn.microsoft.com/ it-it/library/ff878091.aspx – Loudenvier

5

rendere il campo un campo di identità. Il campo otterrà automaticamente il suo valore. È possibile ottenere l'ultimo valore inserito chiamando SCOPE_IDENTITY() o consultando la variabile di sistema @@ IDENTITY

La funzione SCOPE_IDENTITY() è preferibile.

3

Come ha detto DHeer, non c'è assolutamente alcuna corrispondenza esatta. Se si tenta di creare la propria procedura per farlo, si interromperà invariabilmente la scalabilità dell'applicazione.

Le sequenze di Oracle sono altamente scalabili.

OK, lo riprendo leggermente. Se sei davvero disposto a concentrarti sulla concorrenza e sei disposto a prendere numeri fuori ordine, come è possibile con una sequenza, hai una possibilità. Ma dato che sembri piuttosto poco familiare con t-sql, inizierei a cercare altre opzioni quando (trasferire un'app Oracle a MSSS è quello che stai facendo)

Ad esempio, basta generare un GUID nella funzione "nextval". Ciò si ridimensiona.

Oh e NON utilizzare una tabella per tutti i valori, solo per mantenere il valore massimo nella cache. Dovresti bloccarlo per assicurarti di dare valori univoci ed è qui che smetterai di ridimensionare. Dovrai capire se c'è un modo per memorizzare i valori nella memoria e l'accesso programmatico a qualche tipo di blocco leggero - blocchi di memoria, non blocchi di tabelle.

+0

Ci possono essere spazi vuoti in una sequenza Oracle ma fuori uso? – tuinstoel

+0

È giusto nella sequenza DDL. ORDINA Specifica ORDINA per garantire che i numeri di sequenza siano generati in ordine di richiesta. Questa clausola è utile se si utilizzano i numeri di sequenza come timestamp. Garantire l'ordine di solito non è importante per le sequenze utilizzate per generare le chiavi primarie. –

1

Mi auguro che SQL Server abbia questa funzionalità. Farebbe così tante cose più facili.

Ecco come sono arrivato a questo.

Creare una tabella denominata tblIdentities. In questa tabella metti una riga con i valori minimo e massimo e con quale frequenza il numero di sequenza deve essere ripristinato. Inserisci anche il nome di una nuova tabella (chiamala tblMySeqNum). In questo modo, l'aggiunta di più generatori di Sequence Number in seguito sarà abbastanza facile.

tblMySeqNum ha due colonne. ID (che è un'identità int) e InsertDate (che è una colonna di data e ora con un valore predefinito di GetDate()).

Quando è necessario un nuovo numero seq, chiamare uno sproc che si inserisce in questa tabella e utilizzare SCOPE_IDENTITY() per ottenere l'identità creata. Assicurati di non aver superato il massimo in tblIdentities. Se hai quindi restituito un errore. Se non restituisci il tuo numero di sequenza.

Ora, per ripristinare e pulire. Avere un lavoro che viene eseguito regolarmente come necessario che controlla tutte le tabelle elencate in tblIdentites (solo uno per ora) per vedere se devono essere ripristinati. Se hanno raggiunto il valore di reset o il tempo, chiamare DBCC IDENT RESEED sul nome della tabella elencata nella riga (tblMySeqNum in questo esempio). Questo è anche un buon momento per cancellare le righe in più di cui non hai realmente bisogno in quella tabella.

NON eseguire la pulizia o il seeding nella propria sproc che ottiene l'identità. Se lo fai, il tuo generatore di numeri di sequenza non scalerà affatto.

Come ho detto, renderebbe così tante cose più semplici di questa funzionalità in SQL Server, ma ho scoperto che funziona abbastanza bene.

Vaccano

+0

L'amore quando ottengo un voto negativo, ma l'elettore è troppo codardo per dire perché sono giù voto .... – Vaccano

1

Questo potrebbe essere già stata risolta molto tempo fa ... ma da SQL 2005 in poi è possibile utilizzare la funzione ROW_NUMBER ... un esempio potrebbe essere:

select ROW_NUMBER() OVER (ORDER BY productID) as DynamicRowNumber, xxxxxx,xxxxx 

Il OVER la dichiarazione usa lo ORDER BY per la chiave primaria univoca nel mio caso ...

Spero che questo aiuti ... niente più tabelle temporanee, o strani join !!

6

L'identità è la soluzione migliore e più scalabile, MA, se hai bisogno di una sequenza che non sia un incremento int, come 00A, 00B, 00C, o qualche sequenza speciale, c'è un secondo metodo migliore. Se implementato correttamente, viene ridimensionato in modo corretto, ma se implementato male, viene scalato in modo errato. Esito a raccomandarlo, ma quello che fai è:

  1. È necessario memorizzare il "valore successivo" in una tabella. La tabella può essere una semplice tabella, una riga, una colonna con quel valore. Se hai diverse sequenze, possono condividere la tabella, ma potresti ottenere meno contesa disponendo di tabelle separate per ciascuna.
  2. È necessario scrivere una singola istruzione di aggiornamento che incrementerà tale valore di 1 intervallo. È possibile inserire l'aggiornamento in un proc memorizzato per renderlo semplice da usare e impedirne la ripetizione nel codice in luoghi diversi.
  3. Utilizzare la sequenza correttamente, in modo che si riduca in modo ragionevole (no, non così Identitiy :-) richiede due cose: a. l'istruzione di aggiornamento ha una sintassi speciale creata per questo problema esatto che aumenterà e restituirà il valore in una singola istruzione; b. devi recuperare il valore dalla sequenza personalizzata PRIMA dell'inizio di una transazione e al di fuori dell'ambito della transazione. Questa è una delle ragioni per cui Identity scala: restituisce un nuovo valore indipendentemente dall'ambito della transazione, per qualsiasi tentativo di inserimento, ma non ripristina l'errore. Ciò significa che non bloccherà, e significa anche che avrai lacune per le transazioni fallite.

La sintassi di aggiornamento speciale varia un po 'per versione, ma l'essenza è che si esegue un'assegnazione a una variabile e l'aggiornamento nella stessa istruzione. Per il 2008, Itzik Ben-Gan ha questa soluzione pulita: http://www.sqlmag.com/Articles/ArticleID/101339/101339.html?Ad=1

La vecchia scuola 2000 e il metodo seguito si presenta così:

UPDATE sequenzaNavigazione SET @localVar = valore = valore + 5 - cambiare la coda fine alla logica incrementale

Questo incrementerà e restituirà il valore successivo.

Se non si riesce ad avere degli spazi vuoti (resistere a tale requisito :-) allora è tecnicamente possibile mettere quell'aggiornamento o proc a fianco del resto della propria trnsaction, ma si ottiene un GRANDE hit della concorrenza dato che ogni inserto attende il uno da commettere.

Non posso prendermi il merito; Ho imparato tutto da Itzik.

0

Non una risposta esatta, ma oltre ad alcune risposte esistenti

SCOPE_IDENTITY (Transact-SQL)

SCOPE_IDENTITY, IDENT_CURRENT e @@ IDENTITY sono funzioni simili perché restituiscono valori che ar e inserito nelle colonne di identità.

IDENT_CURRENT non è limitato dall'ambito e dalla sessione; è limitato a una tabella specificata . IDENT_CURRENT restituisce il valore generato per una tabella specifica in qualsiasi sessione e qualsiasi ambito. Per ulteriori informazioni, vedere IDENT_CURRENT (Transact-SQL).

Significa due sessioni diverse possono avere uno stesso di un valore di identità o sequenza in modo da evitare questo ed ottenere il numero unico per tutte le sessioni utilizzano IDENT_CURRENT

0

Proprio a causa di questo IDENT_CURRENT non è limitato dalla portata e della sessione ; è limitato a una tabella specificata. abbiamo bisogno di usare SCOPE_IDENTITY() perché l'identità dell'ambito ci darà un numero univoco generato nella nostra sessione e l'unicità è fornita dall'identità stessa.

1

Se è possibile aggiornare a SQL Server 2012, è possibile utilizzare gli oggetti SEQUENCE. Persino SQL Server 2012 Express supporta le sequenze.

CREATE SEQUENCE supplier_seq 
    AS DECIMAL(38) 
    MINVALUE 1 
    MAXVALUE 999999999999999999999999999 
    START WITH 1 
    INCREMENT BY 1 
    CACHE 20; 

SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 
SELECT NEXT VALUE FOR supplier_seq 

Risultati in:

--------------------------------------- 
1 

(1 row(s) affected) 


--------------------------------------- 
2 

(1 row(s) affected) 


--------------------------------------- 
3 

(1 row(s) affected) 


--------------------------------------- 
4 

(1 row(s) affected) 


--------------------------------------- 
5 

(1 row(s) affected) 

Basta prendere cura di specificare il tipo di dati a destra. Se non l'avessi specificato, il MAXVALUE che hai fornito non sarebbe stato accettato, ecco perché ho usato DECIMAL con la massima precisione possibile.

Altro su SEQUENZE qui: http://msdn.microsoft.com/en-us/library/ff878091.aspx