SQL Server: restituisce valore dopo INSERT


318

Sto cercando di recuperare un valore-chiave dopo un'istruzione INSERT. Esempio: ho una tabella con gli attributi name e id. id è un valore generato.

    INSERT INTO table (name) VALUES('bob');

Ora voglio recuperare l'ID nello stesso passaggio. Come si fa?

Stiamo utilizzando Microsoft SQL Server 2008.


Ho trovato una risposta utile qui: [1] [1] [-chiavi generate PreparedStatement-con-dichiarazione-ritorno]: stackoverflow.com/questions/4224228/...
Lars Ladegaard

Risposte:


477

Non è necessario un SELECT separato ...

INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');

Funziona anche con colonne non IDENTITY (come i GUID)


29
potresti elaborare un po '? Dove va l'output in questo esempio? La documentazione mostra solo esempi per le tabelle (usando output ... in). Idealmente, vorrei solo essere in grado di passarlo in una variabile
JonnyRaa

2
@JonnyLeeds: non puoi farlo su una variabile (a meno che una variabile di tabella). L'USCITA va al client o ad un tavolo
gbn

7
Sfortunatamente, non puoi fare affidamento su questo poiché l'aggiunta di un trigger alla tabella interromperà le tue affermazioni! re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
hajikelist

1
@hajikelist: questo è piuttosto un caso limite, SET NCOOUNT ON nel trigger di solito aiuta. Vedi stackoverflow.com/questions/1483732/set-nocount-on-usage
gbn

5
Non usare mai @@ IDENTITY. SCOPE_IDENTITY, sì, ma mai @@ IDENTITY. Non è affidabile
gbn,

188

Utilizzare SCOPE_IDENTITY()per ottenere il nuovo valore ID

INSERT INTO table (name) VALUES('bob');

SELECT SCOPE_IDENTITY()

http://msdn.microsoft.com/en-us/library/ms190315.aspx


7
assumendo che idsia identità
Ilia G

12
@ liho1eye - L'OP si riferiva al nome della colonna identità come id, quindi sì.
Curt,

3
Su un sistema più grande, cosa succede se molti sql vengono eseguiti contemporaneamente? Restituirà l'ultimo ID inserito per ogni richiesta?
Shiv

2
@Shiv "SCOPE_IDENTITY restituisce valori inseriti solo nell'ambito corrente"
goodies4uall

45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

È la scommessa più sicura poiché esiste un problema noto con il conflitto della clausola OUTPUT sui tavoli con trigger. Lo rende abbastanza inaffidabile, anche se al momento il tuo tavolo non ha alcun trigger: qualcuno che ne aggiunge uno lungo la linea interromperà la tua applicazione. Tipo di comportamento Time Bomb.

Vedi l'articolo msdn per una spiegazione più approfondita:

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx


Solo se non aggiungi SET NOCOUNT ON nei trigger. Vedi anche docs.microsoft.com/en-us/sql/database-engine/configure-windows/…
gbn

questa non è un'opzione per i nostri ambienti legacy @gbn
hajikelist

@hajikelist Abbiamo tutti un'eredità, ma il rischio che si inneschi di incasinare OUTPUT è basso, basta solo impostare. Se qualcuno sta aggiungendo un trigger, dovrebbe sapere come codificarlo (implica che hai il controllo principalmente) o devi addestrare i tuoi sviluppatori. Ad un certo punto, sarai costretto a migrare quando quella versione di SQL non è più supportato ecc. in modo che i trigger non causino un gruppo di risultati. In ogni caso, non è la risposta migliore perché se si dispone dei trigger INSTEAD OF, SCOPE_IDENTITY potrebbe non funzionare ( stackoverflow.com/questions/908257/… )
gbn

@gbn - Mi piace solo evitare cose sciocche come questa. Non dirò a tutti i miei sviluppatori, "Non dimenticare di aggiungere" non interrompere la mia app "in ogni trigger." - Puoi tenerlo. Lo scenario "invece" è molto più di un imo caso limite.
Elenco dei marchi di fabbrica del

Una risposta più sicura potrebbe essere solo se l'applicazione esegue un'altra query una volta che ritorna da questa. Fintanto che viene eseguito sul back-end, la penalità prestazionale dovrebbe valere la semplicità di gestione dello sviluppo in gruppi di persone, ed è più vicina in base agli standard rispetto ad alcune funzionalità folli con casi limite. Preferirei che i casi limite fossero nel mio codice ed evitassi sulle piattaforme. solo la mia opinione non va fuori di testa :)
Dan Chase,

33

Entity Framework esegue qualcosa di simile alla risposta di gbn:

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO @generated_keys
VALUES('bob');

SELECT t.[CustomerID]
FROM @generated_keys AS g 
   JOIN dbo.Customers AS t 
   ON g.Id = t.CustomerID
WHERE @@ROWCOUNT > 0

I risultati dell'output vengono archiviati in una variabile di tabella temporanea e quindi selezionati nuovamente sul client. Devi essere consapevole del gotcha:

gli inserti possono generare più di una riga, pertanto la variabile può contenere più di una riga, pertanto è possibile restituirne più di una ID

Non ho idea del perché EF si sarebbe unito al tavolo effimero di nuovo al tavolo reale (in quali circostanze i due non corrisponderebbero).

Ma è quello che fa EF.

Solo SQL Server 2008 o più recente. Se è il 2005, sei sfortunato.


2
Il motivo per cui EF lo fa è assicurarsi che possa anche "vedere" tutte le altre modifiche al Customerrecord inserito , poiché potrebbe esserci altra logica sul lato DB che lo influenza, ad esempio DEFAULTsu alcune colonne, trigger sulla tabella, ecc. EF aggiorna il entità (oggetto) utilizzata per l'inserimento, quindi il lato client ottiene l'oggetto cliente con l'ID e tutto il resto che rappresenta lo stato corrente della riga.
Hilarion,

Un altro motivo per non usare EF.
cskwg

11

@@IDENTITY È una funzione di sistema che restituisce l'ultimo valore di identità inserito.


4
Devo sconsigliarti mai di usare @@ IDENTITY - non è preciso (troppo ampio) molto meno sicuro per i thread - vedi la risposta di @ Curt su SCOPE_IDENTITY ().
zanlok,

10

Esistono molti modi per uscire dopo l'inserimento

Quando si inseriscono dati in una tabella, è possibile utilizzare la clausola OUTPUT per restituire una copia dei dati inseriti nella tabella. La clausola OUTPUT assume due forme base: OUTPUT e OUTPUT INTO. Utilizzare il modulo OUTPUT se si desidera restituire i dati all'applicazione chiamante. Utilizzare il modulo OUTPUT INTO se si desidera restituire i dati a una tabella o a una variabile di tabella.

DECLARE @MyTableVar TABLE (id INT,NAME NVARCHAR(50));

INSERT INTO tableName
(
  NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO @MyTableVar
VALUES
(
   'test',...
)

IDENT_CURRENT : restituisce l'ultima identità creata per una determinata tabella o vista in qualsiasi sessione.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : restituisce l'ultima identità da una stessa sessione e lo stesso ambito. Un ambito è una stored procedure / trigger ecc.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITÀ : restituisce l'ultima identità della stessa sessione.

SELECT @@IDENTITY AS [@@IDENTITY];

1
@RezaJenabi Jun, messo fuori è molto ben fatto, è meglio che trovare molti ID nella tabella. Ho usato out putper bulk inserte insert select statement. grazie del tuo suggerimento
Amirhossein,

5

La soluzione migliore e più sicura sta usando SCOPE_IDENTITY().

Devi solo ottenere l'identità dell'ambito dopo ogni inserimento e salvarla in una variabile perché puoi chiamare due inserti nello stesso ambito.

ident_currente @@identitypossono essere che funzionano ma non sono un ambito sicuro. Puoi avere problemi in una grande applicazione

  declare @duplicataId int
  select @duplicataId =   (SELECT SCOPE_IDENTITY())

Maggiori dettagli sono qui documenti Microsoft


1
Questo può semplificare fino aselect @duplicataId = SCOPE_IDENTITY()
pcnate il

1
OUTPUTla clausola è una soluzione migliore, più pura :)
Dale K,

OUTPUT INTO è molto lento.
cskwg,


1

Esistono diversi modi per ottenere l'ultimo ID inserito dopo il comando insert.

  1. @@IDENTITY : Restituisce l'ultimo valore di identità generato su una connessione nella sessione corrente, indipendentemente dalla tabella e dall'ambito dell'istruzione che ha prodotto il valore
  2. SCOPE_IDENTITY(): Restituisce l'ultimo valore di identità generato dall'istruzione insert nell'ambito corrente nella connessione corrente indipendentemente dalla tabella.
  3. IDENT_CURRENT(‘TABLENAME’): Restituisce l'ultimo valore di identità generato nella tabella specificata indipendentemente da qualsiasi connessione, sessione o ambito. IDENT_CURRENT non è limitato da ambito e sessione; è limitato a una tabella specificata.

Ora sembra più difficile decidere quale sarà la corrispondenza esatta per il mio requisito.

Preferisco principalmente SCOPE_IDENTITY ().

Se si utilizza selezionare SCOPE_IDENTITY () insieme a TableName nell'istruzione insert, si otterrà il risultato esatto secondo le proprie aspettative.

Fonte: CodoBee


0

Questo è il modo in cui utilizzo OUTPUT INSERTED, quando inserisco una tabella che utilizza ID come colonna di identità in SQL Server:

'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)

0

È possibile aggiungere un'istruzione select all'istruzione insert. Integer myInt = Inserisci nella tabella1 (FName) i valori ('Fred'); Seleziona Scope_Identity (); Ciò restituirà un valore dell'identità quando viene eseguito lo scaler.



-4

* L'ordine dei parametri nella stringa di connessione è talvolta importante. * La posizione del parametro Provider può interrompere il cursore recordset dopo aver aggiunto una riga. Abbiamo riscontrato questo comportamento con il provider SQLOLEDB.

Dopo aver aggiunto una riga, i campi della riga non sono disponibili, A MENO CHE il provider sia specificato come primo parametro nella stringa di connessione. Quando il provider si trova in un punto qualsiasi della stringa di connessione, ad eccezione del primo parametro, i campi della riga appena inseriti non sono disponibili. Quando abbiamo spostato il provider sul primo parametro, i campi delle righe sono stati visualizzati magicamente.


1
Potresti dirci come questo commento risponde / è rilevante per la domanda che è stata posta? Non credo che meriti maiuscole / grassetto. Se la tua risposta è ritenuta utile, gli utenti la voteranno.
n__o

Molti utenti probabilmente sono arrivati ​​a questa pagina perché non avevano campi validi per identificare la riga appena aggiunta. Questo comportamento che abbiamo riscontrato (che semplicemente modificando l'ordine dei parametri nella stringa di connessione consente di accedere immediatamente alla riga appena aggiunta) è così bizzarro che ho pensato che meritasse di essere menzionato in maiuscolo, soprattutto perché molto probabilmente risolverà il motivo per cui le persone vogliono il nuovo ID riga e altri campi di quella riga. Mettendo semplicemente il provider come primo parametro, il problema scompare.
David Guidos,

Devi modificare e migliorare la tua risposta. Al momento è rumoroso e non si presenta come una risposta decente o addirittura un tentativo
James

Cosa intendi esattamente con "rumoroso"? Devi spiegare il tuo reclamo. È più semplice che può essere. Se si modifica l'ordine dei parametri nella stringa di connessione, ciò può influire sulla disponibilità dei dati di riga dopo un inserimento.
David Guidos,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.