AUTOINCREMENTO SEM PERDER SEQUENCIA

LUIS.HERRERA 24/04/2013 09:36:13
#422426
Bem pessoal achei esse outro código, como disse na resposta anterior, que faz uso de uma tabela auxiliar só para os IDs das tabelas que serão controladas.

-- Cria a tabela de pessoas
CREATE TABLE tblPessoas (IDPessoa INT NOT NULL, NomePessoa VARCHAR(50))
ALTER TABLE tblPessoas ADD CONSTRAINT PKPessoa PRIMARY KEY (IDPessoa)

-- Cria a tabela de contadores (IDs)
CREATE TABLE tblContadores (NomeTabela VARCHAR(20), Valor INT)
INSERT INTO tblContadores VALUES ([ô]tblPessoas[ô],0)

-- Cria a trigger de inserção
CREATE TRIGGER tblSeqPessoa ON tblPessoas
INSTEAD OF INSERT
AS BEGIN

-- Captura o ID atual (bloqueando-o para que duas pessoas não peguem o mesmo ID)
DECLARE @IDPessoa INT
SET @IDPessoa = (SELECT VALOR FROM tblContadores (XLOCK) WHERE NomeTabela = [ô]tblPessoas[ô])
SET @IDPessoa = @IDPessoa + 1

-- Atualiza o contador
UPDATE tblContadores SET Valor = Valor + 1 WHERE NomeTabela = [ô]tblPessoas[ô]

-- Insere o registro
INSERT INTO tblPessoas (IDPessoa, NomePessoa)
SELECT @IDPessoa AS IDPessoa, NomePessoa FROM INSERTED
END

-- Simula uma inserção
INSERT INTO tblPessoas (NomePessoa) VALUES ([ô]Nome 1[ô])

-- Verifica o registro
SELECT IDPessoa, NomePessoa FROM tblPessoas

-- Verifica o contador
SELECT NomeTabela, Valor FROM tblContadores


O que gostaria de saber é como fazer o código de uma forma genérica, onde eu pudesse informar múltiplas tabelas como origem. Será que terei de ter um Trigger específico para cada tabela? Ou poderia usar o nome da tabela como algum parâmetro passado pelo C#?

[/b]Pergunta: Como funcionaria isso?[/b]
FELLIPEASSIS 25/04/2013 12:08:32
#422484
INSERT INTO CLIENTE (id,nome)--- SEM IDENTITY OU AUTO INCREMENTO
VALUES
(SELECT isnull(max(id),0)+1,[ô]FULANO[ô]) -- SE ESTIVER NULL ELE ACRECENTA ASSIM MESMO LEMBRANDO QUE ESSA PARTE [Ô]SELECT isnull(max(id),0)+1[Ô] REPRESENTA O ACRECIMO DO ID
FROM CLIENTE
LLAIA 25/04/2013 14:26:26
#422491
O grande problema é a questão da duplicidade. Evitando a auto-numeração, vc tem duas opções: trata o erro quando houver duplicidade ou trava a tabela no processo da criação desse identificador tendo que ter um tratamento para os processos que se depararem com a tabela travada.

Já trabalhei com um sistema que tinha uma tabela controladora de IDs para várias outras e era um inferno a duplicidade se não fosse tratado com travas.
FELLIPEASSIS 25/04/2013 14:49:05
#422493
pode usar tratamento de erro
EX: sqlserver

BEGIN TRY
-- o codigo a ser feito

END TRY
BEGIN CATCH
ROLLBACK TRAN
SELECT ERROR_MESSAGE() as RETORNO
END CATCH
LUIS.HERRERA 25/04/2013 15:40:07
#422500
Então LAIA no código que postei acima, dizia no forum que trava a tabela de origem dos IDs acho que é nessa linha:
SET @IDPessoa = (SELECT VALOR FROM tblContadores (XLOCK) WHERE NomeTabela = [ô]tblPessoas[ô])


Mas assim qualquer inserção em tabelas que se utilizem desse recurso seriam tratavas pelo que entendi, e teria de ter um tratamente em cada uma delas.

No VB6 com Access, eu pegava o Max(ID) + 1 e gravava, se desse erro, eu interceptava, checava o código para ver se era de já ter o ID gravado, e mandava reiniciar com Goto na linha que pegava o Max(ID) + 1 novamente., até que conseguisse gravar. Nesse aplicativo nunca tive problema.

Agora quero fazer o mesmo aqui no C# + SQL Server. Como é um gerenciador de bancos, pensei que fosse mais simples e não precisaria tratar no código, mas pelo que estou vendo, vai ser muito mais fácil e [Ô]Seguro[Ô] tratar no código com Max() + 1 do que fazer esse monte de coisas no banco que depois nem terei acesso, só via código e por aplicativos feitos para essa manutenção que dará mais trabalho, já no código basta recompilar e distribuir.

Acho que esse código do Fellipe vai facilitar tudo, pois não faço nada no banco:
INSERT INTO CLIENTE (id,nome)  
VALUES (SELECT isnull(max(id),0)+1,[ô]FULANO[ô])
FROM CLIENTE


Tenho só duas dúvidas:

1- usando o insert acima, como eu obtenho o ID gravado para poder atribuir aos campos das tabelas relacionadas?
No inserte com autoincremento, faço assim:

Int32 novaAtitudeID = 0;
string sql = [Ô]INSERT INTO ATITUDES (ATITUDE,IDIOMA,INATIVO) VALUES (@DESCRICAO,@IDIOMA,@INATIVO); [Ô]
+ [Ô]SELECT CAST(scope_identity() AS int)[Ô];

SqlCommand cmd = new SqlCommand(sql, Dados.cn);
...
novaAtitudeID = (Int32)cmd.ExecuteScalar();


2- Qual é o código de tratamento de erro que é gerado pelo SqlException para este caso do ID já existir?
FELLIPEASSIS 27/04/2013 20:30:45
#422621
desculpa a demora hehe.Bom vc poderia fazer assim.
é uma procedure

BEGIN TRY
DECLARE @cod_novo INT
BEGIN TRAN
--Inserir na Tabela exemplo tabela1
INSERT INTO tabela1 (codigo,nome,data_cad)
VALUES (@codigo,@nome,getdate())

SELECT @cod_novo = codigo

-- inserir tabela tabela2
INSERT INTO tabela2(codigo,telefone,email)
VALUES (@cod_novo,@telefone,@email)

SELECT @cod_novo AS RETORNO --retorno é só o nome da coluna para não ficar em branco

COMMIT TRAN
END TRY

BEGIN CATCH
ROLLBACK TRAN
SELECT ERROR_MESSAGE() AS RETORNO --RETORNO SERÁ O CAMPO ONDE A EXCEÇAO IRÁ EXIBIR
END CATCH

é usado o mesmo codigo em duas tabelas e depois retorna o codigo inserido, ai fica ao seu critério

Página 2 de 2 [16 registro(s)]
Tópico encerrado , respostas não são mais permitidas