SQL Server - Valor de retorno após INSERT


318

Estou tentando recuperar o valor-chave após uma instrução INSERT. Exemplo: eu tenho uma tabela com os atributos nome e id. id é um valor gerado.

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

Agora, quero recuperar o ID na mesma etapa. Como isso é feito?

Estamos usando o Microsoft SQL Server 2008.


Encontrei uma resposta útil aqui: [declaração preparada com chaves de retorno de declaração gerada] [1] [1]: stackoverflow.com/questions/4224228/…
Lars Ladegaard

Respostas:


477

Não há necessidade de um SELECT separado ...

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

Isso funciona também para colunas que não são IDENTITY (como GUIDs)


29
você poderia elaborar um pouco? Para onde vai a saída neste exemplo? A documentação mostra apenas exemplos de tabelas (usando a saída ... para). Idealmente, eu gostaria de apenas ser capaz de passá-lo em uma variável
JonnyRaa

2
@ JonnyLeeds: você não pode fazer isso com uma variável (a menos que seja uma variável de tabela). A saída vai para o cliente ou uma mesa
gbn

7
Infelizmente, você não pode confiar nisso, pois adicionar um gatilho à tabela quebrará suas declarações! re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
hajikelist

1
@hajikelist: este é um caso bastante delicado, SET NCOOUNT ON no gatilho geralmente ajuda. Veja stackoverflow.com/questions/1483732/set-nocount-on-usage
gbn

5
Nunca use @@ IDENTITY. SCOPE_IDENTITY, sim, mas nunca @@ IDENTITY. Não é confiável
gbn

188

Use SCOPE_IDENTITY()para obter o novo valor de ID

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

SELECT SCOPE_IDENTITY()

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


7
assumindo idé a identidade
Ilia G

12
@ liho1eye - O OP se referiu ao nome da coluna de identidade como id, então sim.
Curt

3
Em um sistema maior, e se muitos sqls forem executados ao mesmo tempo? Ele retornará o último ID inserido para cada solicitação?
Shiv

2
@Shiv "SCOPE_IDENTITY retorna valores inseridos somente no escopo atual"
goodies4uall

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

É a aposta mais segura, pois há um problema conhecido no conflito da cláusula OUTPUT em tabelas com gatilhos. Torna isso bastante confiável, mesmo que sua tabela não tenha atualmente nenhum gatilho - alguém adicionando um abaixo da linha quebrará seu aplicativo. Time Bomb tipo de comportamento.

Veja o artigo msdn para uma explicação mais detalhada:

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


Somente se você não adicionar SET NOCOUNT ON nos gatilhos. Consulte também docs.microsoft.com/en-us/sql/database-engine/configure-windows/…
gbn 13/03/18

esta não é uma opção para os nossos ambientes legados @gbn
hajikelist

@hajikelist Todos nós temos legado, mas o risco de gatilhos atrapalharem OUTPUT é baixo, basta apenas ativar a quantidade. Se alguém estiver adicionando um gatilho, ele deve saber codificá-lo (implica que você tem controle principalmente) ou você precisará treinar seus desenvolvedores. Em algum momento, você será forçado a migrar quando a versão do SQL não estiver mais suportado etc., para que os gatilhos não causem um conjunto de resultados. Seja como for, não é a melhor resposta, porque se você tiver acionado INSTEAD OF, o SCOPE_IDENTITY poderá não funcionar ( stackoverflow.com/questions/908257/… )
gbn

@gbn - Eu só gosto de evitar coisas bobas como essa. Não vou dizer a todos os meus desenvolvedores: "Não se esqueça de adicionar o 'não quebre minha declaração de aplicativo' em todos os gatilhos". - você pode ficar com ele. O cenário "em vez disso" é muito mais um exemplo de borda.
hajikelist

Uma resposta mais segura pode ser apenas para que o aplicativo execute outra consulta assim que retornar dessa. Desde que seja feito no back-end, a penalidade de desempenho deve valer a simplicidade de gerenciar o desenvolvimento em grupos de pessoas, e é mais próxima de acordo com os padrões do que algum recurso maluco com casos extremos. Prefiro que os casos extremos estejam no meu código e os evitemos nas plataformas. apenas a minha opinião não freak out :)
Dan perseguição

33

O Entity Framework executa algo semelhante à resposta do 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

Os resultados da saída são armazenados em uma variável de tabela temporária e depois selecionados de volta ao cliente. Precisa estar ciente da pegadinha:

inserções podem gerar mais de uma linha; portanto, a variável pode conter mais de uma linha; assim, você pode retornar mais de uma linha ID

Não tenho idéia de por que a EF uniria a tabela efêmera de volta à tabela real (em que circunstâncias as duas não coincidiriam).

Mas é isso que a EF faz.

SQL Server 2008 ou apenas mais recente. Se for 2005, então você está sem sorte.


2
O motivo pelo qual a EF faz isso é garantir que também possa "ver" todas as outras alterações no Customerregistro inserido , pois pode haver outra lógica do lado do banco de dados afetando-o, por exemplo, DEFAULTem algumas colunas, gatilhos na tabela, etc. entidade (objeto) usada para a inserção, para que o lado do cliente obtenha o objeto do cliente com o ID e tudo o mais que representa o estado atual da linha.
Hilarion

Outro motivo para não usar EF.
cskwg 24/02

11

@@IDENTITY É uma função do sistema que retorna o último valor de identidade inserido.


4
É aconselhável evitar o uso de @@ IDENTITY - não é preciso (muito amplo) e é muito menos seguro para threads - consulte a resposta de Curt sobre SCOPE_IDENTITY ().
precisa saber é o seguinte

10

Existem várias maneiras de sair após a inserção

Ao inserir dados em uma tabela, você pode usar a cláusula OUTPUT para retornar uma cópia dos dados inseridos na tabela. A cláusula OUTPUT assume duas formas básicas: OUTPUT e OUTPUT INTO. Use o formulário OUTPUT se desejar retornar os dados para o aplicativo de chamada. Use o formulário OUTPUT INTO se desejar retornar os dados para uma tabela ou variável de tabela.

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

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

IDENT_CURRENT : retorna a última identidade criada para uma tabela ou exibição específica em qualquer sessão.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : retorna a última identidade de uma mesma sessão e do mesmo escopo. Um escopo é um procedimento armazenado / gatilho etc.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

IDENTIDADE : Retorna a última identidade da mesma sessão.

SELECT @@IDENTITY AS [@@IDENTITY];

1
@RezaJenabi Jun, posto em prática, é muito bom, é melhor do que encontrar muitos IDs na tabela. I utilizado out putpara bulk inserte inserção por select statement. obrigado sua sugestão
Amirhossein

5

A melhor e mais segura solução está sendo usada SCOPE_IDENTITY().

Você só precisa obter a identidade do escopo após cada inserção e salvá-la em uma variável, pois é possível chamar duas inserções no mesmo escopo.

ident_currente @@identitypode ser que eles funcionem, mas não têm escopo seguro. Você pode ter problemas em um grande aplicativo

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

Mais detalhes estão aqui Microsoft docs


1
Pode simplificar isso paraselect @duplicataId = SCOPE_IDENTITY()
pcnate 26/10/1918

1
OUTPUTcláusula é uma solução melhor, mais puro :)
Dale K

OUTPUT INTO é muito lento.
cskwg 24/02


1

Existem várias maneiras de obter o último ID inserido após o comando insert.

  1. @@IDENTITY : Retorna o último valor de identidade gerado em uma conexão na sessão atual, independentemente da tabela e do escopo da instrução que produziu o valor
  2. SCOPE_IDENTITY(): Retorna o último valor de identidade gerado pela instrução insert no escopo atual da conexão atual, independentemente da tabela.
  3. IDENT_CURRENT(‘TABLENAME’): Retorna o último valor de identidade gerado na tabela especificada, independentemente de qualquer conexão, sessão ou escopo. IDENT_CURRENT não é limitado por escopo e sessão; está limitado a uma tabela especificada.

Agora parece mais difícil decidir qual será a correspondência exata para minha exigência.

Eu prefiro SCOPE_IDENTITY ().

Se você usar selecionar SCOPE_IDENTITY () junto com TableName na instrução insert, obterá o resultado exato conforme sua expectativa.

Fonte: CodoBee


0

É assim que eu uso OUTPUT INSERTED, ao inserir em uma tabela que usa ID como coluna de identidade no 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

Você pode anexar uma instrução de seleção à sua instrução de inserção. Número inteiro myInt = Insere valores da tabela1 (FName) ('Fred'); Selecione Scope_Identity (); Isso retornará um valor da identidade quando o scaler executado.



-4

* A ordem dos parâmetros na cadeia de conexão às vezes é importante. * A localização do parâmetro Provider pode interromper o cursor do conjunto de registros após adicionar uma linha. Vimos esse comportamento com o provedor SQLOLEDB.

Depois que uma linha é adicionada, os campos da linha não estão disponíveis, a menos que o provedor seja especificado como o primeiro parâmetro na cadeia de conexão. Quando o provedor estiver em qualquer lugar da cadeia de conexão, exceto como o primeiro parâmetro, os campos de linha recém-inseridos não estarão disponíveis. Quando movemos o provedor para o primeiro parâmetro, os campos da linha apareceram magicamente.


1
Você poderia nos dizer como esse comentário responde / é relevante para a pergunta que foi feita? Não acho que mereça bonés / negrito. Se sua resposta for considerada útil, os usuários votarão nela.
N

Muitos usuários provavelmente acessaram esta página porque não tinham campos válidos para identificar a linha que acabou de adicionar. Esse comportamento que descobrimos (que simplesmente alterar a ordem dos parâmetros na cadeia de conexão permite acessar a linha recém-adicionada imediatamente) é tão bizarro que achei que merecia ser mencionado nos caps, especialmente porque provavelmente corrigirá o motivo pelo qual as pessoas desejam o novo ID da linha e outros campos dessa linha. Simplesmente colocando o provedor como o primeiro parâmetro, o problema desaparece.
David Guidos

Você precisa editar e melhorar sua resposta. É actualmente barulhento e não se deparar como uma resposta decente ou mesmo uma tentativa
James

O que exatamente você quer dizer com "barulhento"? Você precisa explicar sua reclamação. É o mais simples possível. Se você alterar a ordem dos parâmetros na cadeia de conexão, isso poderá afetar a disponibilidade dos dados da linha após uma inserção.
David Guidos
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.