Como você implementaria seqüências no Microsoft SQL Server?

Alguém tem uma boa maneira de implementar algo como uma seqüência no SQL Server?

Às vezes você não quer usar um GUID, além do fato de que eles são feios como diabos. Talvez a sequência que você quer não seja numérica? Além disso, inserindo uma linha e, em seguida, perguntando ao DB o que o número é apenas parece tão hackish.

O Sql Server 2012 introduziu objects SEQUENCE , que permitem gerar valores numéricos sequenciais não associados a nenhuma tabela.

Criando-os são fáceis:

 CREATE SEQUENCE Schema.SequenceName AS int INCREMENT BY 1 ; 

Um exemplo de usá-los antes da inserção:

 DECLARE @NextID int ; SET @NextID = NEXT VALUE FOR Schema.SequenceName; -- Some work happens INSERT Schema.Orders (OrderID, Name, Qty) VALUES (@NextID, 'Rim', 2) ; 

Veja meu blog para ver em detalhes como usar seqüências:

http://sqljunkieshare.com/2011/12/11/sequences-in-sql-server-2012-implementingmanaging-performance/

Como o sqljunkieshare disse corretamente, a partir do SQL Server 2012 existe um recurso SEQUENCE embutido.

A pergunta original não esclarece, mas eu suponho que os requisitos para a Sequência são:

  1. Tem que fornecer um conjunto de números únicos crescentes
  2. Se vários usuários solicitarem o próximo valor da seqüência simultaneamente, todos eles deverão obter valores diferentes. Em outras palavras, a exclusividade dos valores gerados é garantida, não importa o quê.
  3. Devido à possibilidade de algumas transactions poderem ser revertidas, é possível que o resultado final dos números gerados tenha lacunas.

Eu gostaria de comentar a afirmação na pergunta original:

“Além disso, inserindo uma linha e, em seguida, perguntando ao DB o que o número parece tão invasivo.”

Bem, não há muito que possamos fazer sobre isso aqui. O database é um provedor dos números seqüenciais e o database trata de todos esses problemas de simultaneidade que você não consegue resolver. Eu não vejo alternativa para pedir ao DB o próximo valor da sequência. Tem que haver uma operação atômica “me dê o próximo valor da seqüência” e somente o DB pode fornecer tal operação atômica . Nenhum código de cliente pode garantir que ele é o único que trabalha com a sequência.

Para responder a pergunta no título “como você implementaria seqüências” – Estamos usando 2008, que não tem o recurso SEQUENCE , então depois de algumas leituras sobre este tópico eu terminei com o seguinte.

Para cada sequência que preciso, eu crio uma tabela auxiliar separada com apenas uma coluna IDENTITY (da mesma forma que em 2012 você criaria um object Sequence separado).

 CREATE TABLE [dbo].[SequenceContractNumber] ( [ContractNumber] [int] IDENTITY(1,1) NOT NULL, CONSTRAINT [PK_SequenceContractNumber] PRIMARY KEY CLUSTERED ([ContractNumber] ASC) ) 

Você pode especificar o valor inicial e incrementá-lo. Em seguida, criei um procedimento armazenado que retornaria o próximo valor da sequência. O procedimento inicia uma transação, insere uma linha na tabela auxiliar, lembra o valor de identidade gerado e recupera a transação. Assim, a tabela auxiliar permanece sempre vazia.

 CREATE PROCEDURE [dbo].[GetNewContractNumber] AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE @Result int = 0; IF @@TRANCOUNT > 0 BEGIN -- Procedure is called when there is an active transaction. -- Create a named savepoint -- to be able to roll back only the work done in the procedure. SAVE TRANSACTION ProcedureGetNewContractNumber; END ELSE BEGIN -- Procedure must start its own transaction. BEGIN TRANSACTION ProcedureGetNewContractNumber; END; INSERT INTO dbo.SequenceContractNumber DEFAULT VALUES; SET @Result = SCOPE_IDENTITY(); -- Rollback to a named savepoint or named transaction ROLLBACK TRANSACTION ProcedureGetNewContractNumber; RETURN @Result; END 

Algumas notas sobre o procedimento.

Primeiro, não era óbvio como inserir uma linha em uma tabela que tivesse apenas uma coluna de identidade. A resposta é DEFAULT VALUES .

Então, eu queria que o procedimento funcionasse corretamente se fosse chamado dentro de outra transação. O ROLLBACK simples reverte tudo se houver transactions aninhadas. No meu caso, eu preciso reverter apenas o INSERT para a tabela auxiliar, então usei o SAVE TRANSACTION .

ROLLBACK TRANSACTION sem um savepoint_name ou transaction_name retorna ao início da transação. Ao aninhar transactions, essa mesma instrução reverte todas as transactions internas para a instrução BEGIN TRANSACTION mais externa.

É assim que eu uso o procedimento (dentro de outro procedimento grande que, por exemplo, cria um novo contrato):

 DECLARE @VarContractNumber int; EXEC @VarContractNumber = dbo.GetNewContractNumber; 

Tudo funciona bem se você precisa gerar valores de sequência um de cada vez. No caso de contratos, cada contrato é criado individualmente, portanto, essa abordagem funciona perfeitamente. Posso ter certeza de que todos os contratos sempre têm números de contrato exclusivos.

NB: Apenas para evitar possíveis perguntas. Esses números de contrato são adicionais à chave de identidade substituta que minha tabela Contratos possui. A chave substituta é a chave interna usada para integridade referencial. O número do contrato gerado é um número amigável que é impresso no contrato. Além disso, a mesma tabela de contratos contém os contratos finais e as propostas, que podem se tornar contratos ou permanecer como propostas para sempre. Ambas as Propostas e Contratos possuem dados muito semelhantes, por isso são mantidos na mesma tabela. A proposta pode se tornar um contrato simplesmente mudando a bandeira em uma linha. As propostas são numeradas usando uma sequência separada de números, para os quais tenho uma segunda tabela, SequenceProposalNumber e o segundo, GetNewProposalNumber .


Recentemente, porém, me deparei com um problema. Eu precisava gerar valores de seqüência em um lote, em vez de um por um.

Preciso de um procedimento que processe todos os pagamentos que foram recebidos durante um determinado trimestre de uma só vez. O resultado desse processamento pode ser de aproximadamente 20.000 transactions que desejo registrar na tabela Transactions . Eu tenho um design semelhante aqui. Transactions tabela de Transactions possui uma coluna IDENTITY interna que o usuário final nunca vê e possui um Número de Transação amigável ao usuário que seria impresso na instrução. Então, eu preciso de uma maneira de gerar um determinado número de valores únicos em um lote.

Essencialmente, usei a mesma abordagem, mas há poucas peculiaridades.

Primeiro, não há maneira direta de inserir várias linhas em uma tabela com apenas uma coluna IDENTITY . Embora haja uma solução alternativa (ab) usando MERGE , eu não usei isso no final. Decidi que era mais fácil adicionar uma coluna fictícia de Filler . Minha tabela Sequence estará sempre vazia, então a coluna extra não importa realmente.

A tabela auxiliar é assim:

 CREATE TABLE [dbo].[SequenceS2TransactionNumber] ( [S2TransactionNumber] [int] IDENTITY(1,1) NOT NULL, [Filler] [int] NULL, CONSTRAINT [PK_SequenceS2TransactionNumber] PRIMARY KEY CLUSTERED ([S2TransactionNumber] ASC) ) 

O procedimento é assim:

 -- Description: Returns a list of new unique S2 Transaction numbers of the given size -- The caller should create a temp table #NewS2TransactionNumbers, -- which would hold the result CREATE PROCEDURE [dbo].[GetNewS2TransactionNumbers] @ParamCount int -- not NULL AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; SET XACT_ABORT ON; IF @@TRANCOUNT > 0 BEGIN -- Procedure is called when there is an active transaction. -- Create a named savepoint -- to be able to roll back only the work done in the procedure. SAVE TRANSACTION ProcedureGetNewS2TransactionNos; END ELSE BEGIN -- Procedure must start its own transaction. BEGIN TRANSACTION ProcedureGetNewS2TransactionNos; END; DECLARE @VarNumberCount int; SET @VarNumberCount = ( SELECT TOP(1) dbo.Numbers.Number FROM dbo.Numbers ORDER BY dbo.Numbers.Number DESC ); -- table variable is not affected by the ROLLBACK, so use it for temporary storage DECLARE @TableTransactionNumbers table ( ID int NOT NULL ); IF @VarNumberCount >= @ParamCount BEGIN -- the Numbers table is large enough to provide the given number of rows INSERT INTO dbo.SequenceS2TransactionNumber (Filler) OUTPUT inserted.S2TransactionNumber AS ID INTO @TableTransactionNumbers(ID) -- save generated unique numbers into a table variable first SELECT TOP(@ParamCount) dbo.Numbers.Number FROM dbo.Numbers OPTION (MAXDOP 1); END ELSE BEGIN -- the Numbers table is not large enough to provide the given number of rows -- expand the Numbers table by cross joining it with itself INSERT INTO dbo.SequenceS2TransactionNumber (Filler) OUTPUT inserted.S2TransactionNumber AS ID INTO @TableTransactionNumbers(ID) -- save generated unique numbers into a table variable first SELECT TOP(@ParamCount) n1.Number FROM dbo.Numbers AS n1 CROSS JOIN dbo.Numbers AS n2 OPTION (MAXDOP 1); END; /* -- this method can be used if the SequenceS2TransactionNumber -- had only one identity column MERGE INTO dbo.SequenceS2TransactionNumber USING ( SELECT * FROM dbo.Numbers WHERE dbo.Numbers.Number < = @ParamCount ) AS T ON 1 = 0 WHEN NOT MATCHED THEN INSERT DEFAULT VALUES OUTPUT inserted.S2TransactionNumber -- return generated unique numbers directly to the caller ; */ -- Rollback to a named savepoint or named transaction ROLLBACK TRANSACTION ProcedureGetNewS2TransactionNos; IF object_id('tempdb..#NewS2TransactionNumbers') IS NOT NULL BEGIN INSERT INTO #NewS2TransactionNumbers (ID) SELECT TT.ID FROM @TableTransactionNumbers AS TT; END END 

E é assim que é usado (dentro de algum procedimento armazenado grande que calcula transactions):

 -- Generate a batch of new unique transaction numbers -- and store them in #NewS2TransactionNumbers DECLARE @VarTransactionCount int; SET @VarTransactionCount = ... CREATE TABLE #NewS2TransactionNumbers(ID int NOT NULL); EXEC dbo.GetNewS2TransactionNumbers @ParamCount = @VarTransactionCount; -- use the generated numbers... SELECT ID FROM #NewS2TransactionNumbers AS TT; 

Há poucas coisas aqui que exigem explicação.

Eu preciso inserir um determinado número de linhas na tabela SequenceS2TransactionNumber . Eu uso uma tabela de Numbers ajuda para isso. Esta tabela simplesmente contém números inteiros de 1 a 100.000. É usado em outros lugares do sistema também. Eu verifico se há linhas suficientes na tabela Numbers e a expanda para 100.000 x 100.000 cruzando-se com ela, se necessário.

Eu tenho que salvar o resultado da inserção em massa em algum lugar e passá-lo para o chamador de alguma forma. Uma maneira de passar uma tabela fora do procedimento armazenado é usar uma tabela temporária. Não consigo usar o parâmetro com valor de tabela aqui, porque ele é somente leitura, infelizmente. Além disso, não posso inserir diretamente os valores de sequência gerados na tabela temporária #NewS2TransactionNumbers . Eu não posso usar #NewS2TransactionNumbers na cláusula OUTPUT , porque o ROLLBACK irá limpá-lo. Felizmente, as variables ​​da tabela não são afetadas pelo ROLLBACK .

Então, eu uso a variável de tabela @TableTransactionNumbers como um destino da cláusula OUTPUT . Então eu ROLLBACK a transação para limpar a tabela de seqüências. Em seguida, copie os valores de sequência gerados da variável de tabela @TableTransactionNumbers para a tabela temporária #NewS2TransactionNumbers , porque somente a tabela temporária #NewS2TransactionNumbers pode ser visível para o #NewS2TransactionNumbers pela chamada do procedimento armazenado. A variável de tabela @TableTransactionNumbers não está visível para o chamador do procedimento armazenado.

Além disso, é possível usar a cláusula OUTPUT para enviar a sequência gerada diretamente ao chamador (como você pode ver na variante comentada que usa MERGE ). Ele funciona bem sozinho, mas eu precisava dos valores gerados em alguma tabela para processamento adicional no procedimento armazenado de chamada. Quando eu tentei algo assim:

 INSERT INTO @TableTransactions (ID) EXEC dbo.GetNewS2TransactionNumbers @ParamCount = @VarTransactionCount; 

Eu estava recebendo um erro

Não é possível usar a instrução ROLLBACK em uma instrução INSERT-EXEC.

Mas, eu preciso de ROLLBACK dentro do EXEC , por isso acabei tendo tantas tabelas temporárias.

Depois de tudo isso, como seria bom mudar para a versão mais recente do SQL Server que possui um object SEQUENCE adequado.

Uma coluna de identidade é mais ou menos análoga a uma sequência.

Você poderia simplesmente usar tabelas antigas simples e usá-las como sequências. Isso significa que suas inserções sempre seriam:

 BEGIN TRANSACTION SELECT number from plain old table.. UPDATE plain old table, set the number to be the next number INSERT your row COMMIT 

Mas não faça isso. O bloqueio seria ruim …

Eu comecei no SQL Server e para mim, o esquema de “sequência” do Oracle parecia um hack. Eu acho que você está vindo de outra direção e para você, e scope_identity () parece um hack.

Deixe isso para trás. Quando em Roma, faça como os romanos.

A maneira que eu usei para resolver esse problema foi uma tabela ‘Sequences’ que armazena todas as minhas seqüências e um procedimento armazenado ‘nextval’.

Tabela Sql:

 CREATE TABLE Sequences ( name VARCHAR(30) NOT NULL, value BIGINT DEFAULT 0 NOT NULL, CONSTRAINT PK_Sequences PRIMARY KEY (name) ); 

O PK_Sequences é usado apenas para ter certeza de que nunca haverá seqüências com o mesmo nome.

Procedimento Armazenado SQL:

 IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'nextVal') AND type in (N'P', N'PC')) DROP PROCEDURE nextVal; GO CREATE PROCEDURE nextval @name VARCHAR(30) AS BEGIN DECLARE @value BIGINT BEGIN TRANSACTION UPDATE Sequences SET @value=value=value + 1 WHERE name = @name; -- SELECT @value=value FROM Sequences WHERE name=@name COMMIT TRANSACTION SELECT @value AS nextval END; 

Insira algumas seqüências:

 INSERT INTO Sequences(name, value) VALUES ('SEQ_Workshop', 0); INSERT INTO Sequences(name, value) VALUES ('SEQ_Participant', 0); INSERT INTO Sequences(name, value) VALUES ('SEQ_Invoice', 0); 

Finalmente, obtenha o próximo valor de uma sequência

 execute nextval 'SEQ_Participant'; 

Algum código c # para obter o próximo valor da tabela Sequence,

 public long getNextVal() { long nextval = -1; SqlConnection connection = new SqlConnection("your connection string"); try { //Connect and execute the select sql command. connection.Open(); SqlCommand command = new SqlCommand("nextval", connection); command.CommandType = CommandType.StoredProcedure; command.Parameters.Add("@name", SqlDbType.NVarChar).Value = "SEQ_Participant"; nextval = Int64.Parse(command.ExecuteScalar().ToString()); command.Dispose(); } catch (Exception) { } finally { connection.Dispose(); } return nextval; } 

No SQL Server 2012, você pode simplesmente usar

 CREATE SEQUENCE 

Em 2005 e 2008, você pode obter uma lista arbitrária de números seqüenciais usando uma expressão de tabela comum.

Aqui está um exemplo (note que a opção MAXRECURSION é importante):

 DECLARE @MinValue INT = 1; DECLARE @MaxValue INT = 1000; WITH IndexMaker (IndexNumber) AS ( SELECT @MinValue AS IndexNumber UNION ALL SELECT IndexNumber + 1 FROM IndexMaker WHERE IndexNumber < @MaxValue ) SELECT IndexNumber FROM IndexMaker ORDER BY IndexNumber OPTION (MAXRECURSION 0) 

Seqüências como implementadas pelo Oracle requerem uma chamada para o database antes da inserção. identidades como implementadas pelo SQL Server requerem uma chamada para o database após a inserção.

Um não é mais agressivo do que o outro. O efeito líquido é o mesmo – uma confiança / dependência no armazenamento de dados para fornecer valores exclusivos de chave artificial e (na maioria dos casos) duas chamadas para a loja.

Estou assumindo que seu modelo relacional é baseado em chaves artificiais e, nesse contexto, vou oferecer a seguinte observação:

Nunca devemos procurar imbuir de significado as chaves artificiais; seu único objective deve ser vincular registros relacionados.

Qual é a sua necessidade relacionada ao pedido de dados? pode ser tratado na exibição (apresentação) ou é um atributo verdadeiro de seus dados que deve ser persistido?

Crie uma tabela de estágio com um identificador nela.

Antes de carregar a tabela de estágios, trunque e propague novamente o identificador para começar em 1.

Carregue sua mesa. Cada linha agora tem um valor único de 1 a N.

Crie uma tabela que contenha números de sequência. Isso pode ser várias linhas, uma para cada sequência.

Pesquise o número de sequência na tabela de sequências que você criou. Atualize o número da sequência adicionando o número de linhas na tabela de palco ao número de sequência.

Atualize o identificador da tabela de estágio, adicionando o número de seqüência que você consultou. Este é um processo fácil de um passo. ou Carregue sua tabela de destino, adicione o número de sequência ao identificador enquanto você carrega no ETL. Isso pode aproveitar o carregador em massa e permitir outras transformações.

Considere o seguinte trecho.

 CREATE TABLE [SEQUENCE]( [NAME] [varchar](100) NOT NULL, [NEXT_AVAILABLE_ID] [int] NOT NULL, CONSTRAINT [PK_SEQUENCES] PRIMARY KEY CLUSTERED ( [NAME] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO CREATE PROCEDURE CLAIM_IDS (@sequenceName varchar(100), @howMany int) AS BEGIN DECLARE @result int update SEQUENCE set @result = NEXT_AVAILABLE_ID, NEXT_AVAILABLE_ID = NEXT_AVAILABLE_ID + @howMany where Name = @sequenceName Select @result as AVAILABLE_ID END GO 

Como afirma o sqljunkiesshare , as sequências foram adicionadas ao SQL Server 2012. Veja como fazer isso na GUI. Este é o equívoco de:

 CREATE SEQUENCE Schema.SequenceName AS int INCREMENT BY 1 ; 
  1. No Pesquisador de Objetos , expanda a pasta Programmability
  2. Na pasta Programmability , clique com o botão direito do mouse na pasta Sequences, conforme mostrado abaixo:

insira a descrição da imagem aqui

  1. Sublinhados são os valores que você atualizaria para obter o equivalente da instrução SQL acima, no entanto, eu consideraria alterá-los dependendo de suas necessidades (veja as notas abaixo).

insira a descrição da imagem aqui

Notas:

  • O valor inicial padrão, valor mínimo e valor máximo foram determinados pelo intervalo do tipo de dados que era um int neste caso. Veja aqui mais intervalos de tipos de dados se você quiser usar algo diferente de um int .

  • É uma boa chance que você queira que sua sequência comece em 1 e você pode querer um valor mínimo igual a 1 também.

Eu concordo totalmente e fiz isso no ano passado em um projeto.

Acabei de criar uma tabela com o nome da sequência, valor atual e valor de incremento.

Então eu criei um 2 procs para adicionar e excluí-los. E 2 funções para chegar a próxima, e ficar atual.

Se você quiser inserir dados com uma chave seqüencial, mas não quiser consultar o database novamente para obter a chave inserida, acho que suas duas únicas opções são:

  1. Executar a inserção por meio de um procedimento armazenado que retorna o valor da chave recém-inserida
  2. Implemente a sequência do lado do cliente (para que você conheça a nova chave antes de inserir)

Se estou fazendo a geração de chaves do lado do cliente, adoro os GUIDs. Eu acho que eles são lindos como diabos.

 row["ID"] = Guid.NewGuid(); 

Essa linha deveria estar no capô de um carro esportivo em algum lugar.

Se você estiver usando o SQL Server 2005, você tem a opção de usar Row_Number

O outro problema com as colunas de identidade é que, se você tiver mais de uma tabela em que os números de sequência precisam ser exclusivos, uma coluna de identidade não funcionará. E, como Corey Trager menciona, um tipo de implementação de sequenciamento pode apresentar alguns problemas de bloqueio.

As soluções mais diretamente equivalentes parecem ser criar uma tabela do SQL Server com uma única coluna para a identidade, que substitui um tipo separado de object “sequência”. Por exemplo, se no Oracle você tivesse duas tabelas de uma sequência como Dogs < - sequence object -> Cats, então no SQL Server você criaria três objects de database, todas as tabelas como Dogs < - Pets with identity column -> Gatos. Você poderia inserir uma linha na tabela Pets para obter o número de seqüência em que normalmente usaria NEXTVAL e depois inserir na tabela Dogs or Cats como faria normalmente depois de obter o tipo de animal de estimação real do usuário. Quaisquer colunas comuns adicionais poderiam ser movidas das tabelas Cães / Gatos para a tabela Supertipo de Pets, com algumas conseqüências que 1) haveria uma linha para cada número de seqüência, 2) colunas que não pudessem ser preenchidas ao obter o número de seqüência precisa ter valores padrão e 3) exigiria uma junit para obter todas as colunas.

Por SQL você pode usar essa estratégia;

 CREATE SEQUENCE [dbo].[SequenceFile] AS int START WITH 1 INCREMENT BY 1 ; 

e leia o próximo valor exclusivo com esse SQL

 SELECT NEXT VALUE FOR [dbo].[SequenceFile] 

TRANSAÇÃO SEGURA! Para versões do SQL Server antes de 2012 … (obrigado Matt G.) Uma coisa que falta nesta discussão é a segurança das transactions. Se você obtiver um número de uma sequência, esse número deverá ser exclusivo e nenhum outro aplicativo ou código poderá obter esse número. No meu caso, muitas vezes retiramos números únicos de sequências, mas a transação real pode se estender por um tempo considerável, portanto não queremos que ninguém obtenha o mesmo número antes de confirmarmos a transação. Precisávamos imitar o comportamento das sequências oraculares , onde um número era reservado quando era puxado. Minha solução é usar o xp_cmdshell para obter uma session / transação separada no database, para que possamos atualizar imediatamente a sequência, para o database inteiro, antes mesmo que a transação seja concluída.

 --it is used like this: -- use the sequence in either insert or select: Insert into MyTable Values (NextVal('MySequence'), 'Foo'); SELECT NextVal('MySequence'); --you can make as many sequences as you want, by name: SELECT NextVal('Mikes Other Sequence'); --or a blank sequence identifier SELECT NextVal(''); 

A solução requer uma única tabela para manter valores de seqüência usados ​​e um procedimento que cria uma segunda transação autônoma para garantir que as sessões simultâneas não fiquem emaranhadas. Você pode ter quantas seqüências únicas quiser, elas são referenciadas pelo nome. O código de exemplo abaixo é modificado para omitir o usuário solicitante e o carimbo de data na tabela de histórico de sequência (para auditoria), mas achei que menos complexo era melhor para o exemplo ;-).

  CREATE TABLE SequenceHolder(SeqName varchar(40), LastVal int); GO CREATE function NextVAL(@SEQname varchar(40)) returns int as begin declare @lastval int declare @barcode int; set @lastval = (SELECT max(LastVal) FROM SequenceHolder WHERE SeqName = @SEQname); if @lastval is null set @lastval = 0 set @barcode = @lastval + 1; --=========== USE xp_cmdshell TO INSERT AND COMMINT NOW, IN A SEPERATE TRANSACTION ============================= DECLARE @sql varchar(4000) DECLARE @cmd varchar(4000) DECLARE @recorded int; SET @sql = 'INSERT INTO SequenceHolder(SeqName, LastVal) VALUES (''' + @SEQname + ''', ' + CAST(@barcode AS nvarchar(50)) + ') ' SET @cmd = 'SQLCMD -S ' + @@servername + ' -d ' + db_name() + ' -Q "' + @sql + '"' EXEC master..xp_cmdshell @cmd, 'no_output' --=============================================================================================================== -- once submitted, make sure our value actually stuck in the table set @recorded = (SELECT COUNT(*) FROM SequenceHolder WHERE SeqName = @SEQname AND LastVal = @barcode); --TRIGGER AN ERROR IF (@recorded != 1) return cast('Barcode was not recorded in SequenceHolder, xp_cmdshell FAILED!! [' + @cmd +']' as int); return (@barcode) end GO COMMIT; 

Agora, para que o procedimento funcione, você precisará habilitar o xp_cmdshell, há muitas descrições de como fazer isso, aqui estão minhas annotations pessoais que eu fiz quando estava tentando fazer as coisas funcionarem. A idéia básica é que você precisa do xp_cmdshell ativado no SQLServer Surface. É uma configuração e você precisa definir uma conta de usuário como a conta na qual o comando xp_cmdshell será executado, que acessará o database para inserir o número de sequência e confirmá-lo.

 --- LOOSEN SECURITY SO THAT xp_cmdshell will run ---- To allow advanced options to be changed. EXEC sp_configure 'show advanced options', 1 GO ---- To update the currently configured value for advanced options. RECONFIGURE GO ---- To enable the feature. EXEC sp_configure 'xp_cmdshell', 1 GO ---- To update the currently configured value for this feature. RECONFIGURE GO —-Run SQLServer Management Studio as Administrator, —- Login as domain user, not sqlserver user. --MAKE A DATABASE USER THAT HAS LOCAL or domain LOGIN! (not SQL server login) --insure the account HAS PERMISSION TO ACCESS THE DATABASE IN QUESTION. (UserMapping tab in User Properties in SQLServer) —grant the following GRANT EXECUTE on xp_cmdshell TO [domain\user] —- run the following: EXEC sp_xp_cmdshell_proxy_account 'domain\user', 'pwd' --alternative to the exec cmd above: create credential ##xp_cmdshell_proxy_account## with identity = 'domain\user', secret = 'pwd' -—IF YOU NEED TO REMOVE THE CREDENTIAL USE THIS EXEC sp_xp_cmdshell_proxy_account NULL; -—ways to figure out which user is actually running the xp_cmdshell command. exec xp_cmdshell 'whoami.exe' EXEC xp_cmdshell 'osql -E -Q"select suser_sname()"' EXEC xp_cmdshell 'osql -E -Q"select * from sys.login_token"'