• advertisement_alt
  • advertisement_alt
  • advertisement_alt
Janio Aguiar

SQL - Inserir numero ÚNICO

16 posts in this topic

Boa noite,

Por razões que não vem ao caso, possuo numa tabela um campo que não é AUTOINCREMENT, mas que precisa obrigatoriamente ser único.

Alguém teria um script em SQL que garanta essa inserção SEQUENCIAL e ÚNICA?

Janio

Share this post


Link to post
Share on other sites

Uso esta função em xHarbour para numeração de pedidos, e há quase trinta anos não falha !

 

Function NovoUsu

Local a:= {}, b:= {}, c, d

oSql:= SR_GetConnection()

do while .t.

   cSql:= "lock tables cdpar000 write"

   oSql:Execute(cSql,.f.)

   oSql:Commit()  

   cSql:= "select numnum from cdpar000"

   oSql:Exec(cSql,.t.,.t.,@a,,) 

    c:= a[1,1] + 1

   cSql:= "update cdpar000 set numnum = " + sr_cdbvalue( c )

   oSql:Execute(cSql,.f.)

   oSql:Commit()

   cSql:= "unlock tables"

   oSql:Execute(cSql,.f.)

   oSql:Commit()  

   cSql:= "select usuusu from cdusu000 where usuusu = " + sr_cdbvalue( c )

   oSql:Exec(cSql,.t.,.t.,@b,,) 

   if len(b) > 0

      loop

   endif      

   exit

enddo

return c

 

1 person likes this

Share this post


Link to post
Share on other sites
3 horas atrás, Janio Aguiar disse:

Boa noite,

Por razões que não vem ao caso, possuo numa tabela um campo que não é AUTOINCREMENT, mas que precisa obrigatoriamente ser único.

Alguém teria um script em SQL que garanta essa inserção SEQUENCIAL e ÚNICA?

Janio

Jânio um select max(id) + 1 não te resolve? você pode colocar isso no próprio insert

Share this post


Link to post
Share on other sites
1 hour ago, Vilani said:

Jânio um select max(id) + 1 não te resolve? você pode colocar isso no próprio insert

Vilani, 

Sim e Nao! Se duas (ou mais) estações fizerem o select max(id) no mesmo milésimo de segundo, pode ocorrer, como ja aconteceu de a numeração se repetir. A nao ser q esse select max ja no proprio insert seja melhor e eu gostaria de testar. Tem como demonstrar como seria esse insert no select?

Share this post


Link to post
Share on other sites

no sqlserver nunca tive problemas, o que precisa analisar é tamanho da tabela, índices para não perder performance
seria algo assim:
insert into table (id, nome,...) values(isnull(select max(id),0) + 1, 'joao da silva', '')

2 people like this

Share this post


Link to post
Share on other sites

Precisei disso uma vez, e fiz uma query:
SELECT 
    MAX(empresas.CODEMPRESA) AS maximum_CODEMPRESA
FROM 
    empresas

Depois:
 

HExecuteQuery(QRY_MAXIMOREGISTROEMPRESA,hQueryDefault)

IF empresas.CODEMPRESA = 0 THEN
    empresas.CODEMPRESA = QRY_MAXIMOREGISTROEMPRESA.maximum_CODEMPRESA + 1    
ELSE
END

 

Share this post


Link to post
Share on other sites

Bom dia. Eu colocaria o Select(max) +1 numa trigger before insert

 

Share this post


Link to post
Share on other sites
13 hours ago, Edson Alamino G. Júnior said:

Uso esta função em xHarbour para numeração de pedidos, e há quase trinta anos não falha !

 



Function NovoUsu



Local a:= {}, b:= {}, c, d



oSql:= SR_GetConnection()



do while .t.



   cSql:= "lock tables cdpar000 write"



   oSql:Execute(cSql,.f.)



   oSql:Commit()  



   cSql:= "select numnum from cdpar000"



   oSql:Exec(cSql,.t.,.t.,@a,,) 



    c:= a[1,1] + 1



   cSql:= "update cdpar000 set numnum = " + sr_cdbvalue( c )



   oSql:Execute(cSql,.f.)



   oSql:Commit()



   cSql:= "unlock tables"



   oSql:Execute(cSql,.f.)



   oSql:Commit()  



   cSql:= "select usuusu from cdusu000 where usuusu = " + sr_cdbvalue( c )



   oSql:Exec(cSql,.t.,.t.,@b,,) 



   if len(b) > 0



      loop



   endif      



   exit



enddo



return c

 

Vlw, Edson!
Irei tentar fazer uma adaptação. Se bem que, ao que parece, vc usa uma tabela auxilar com unico registro onde vc guarda o ultimo numero das suas tabelas. Nao eh bem dessa forma que faço, mas tudo bem. Vlw

Janio

Share this post


Link to post
Share on other sites
10 hours ago, Vilani said:

insert into table (id, nome,...) values(isnull(select max(id),0) + 1, 'joao da silva', '')

Essa construção nao eh permitida no mysql. Dando erro aqui

Share this post


Link to post
Share on other sites
2 hours ago, Ananias Guerra said:

Precisei disso uma vez, e fiz uma query:
SELECT 
    MAX(empresas.CODEMPRESA) AS maximum_CODEMPRESA
FROM 
    empresas

Depois:
 

HExecuteQuery(QRY_MAXIMOREGISTROEMPRESA,hQueryDefault)

IF empresas.CODEMPRESA = 0 THEN
    empresas.CODEMPRESA = QRY_MAXIMOREGISTROEMPRESA.maximum_CODEMPRESA + 1    
ELSE
END

 

Ananias,
Vc bloqueia a tabela antes?
Da forma q vc colocou acredito que possa haver repetições quando varias estações estao tentando escrever na mesma tabela ao mesmo tempo

Share this post


Link to post
Share on other sites

Amigo,

Utilize o que normalmente o Banco oferece, a função de agregação serve sim, mas o script não pode ser apenas para saber qual o maior número único. Em postgres temos um tipo especial de objeto denominado sequencia a qual pode ser manipulada simplesmente dizendo para o "DEFAULT" da coluna da tabela que pegue o próximo valor da sequência. Agora caso não seja PostgreSQL, atente para alguns detalhes. 

1. Isolar a tabela

2. Pegar o maior valor (normalmente em Postgres você faz isso com apenas uma sentença SELECT adicionando uma cláusula FOR UPDATE;

3. Inserir na tabela o Registro pretendido já utilizando o max + 1 obtido na sentença anterior

4. Liberar o isolamento (Commit da vida)

Att

 

1 person likes this

Share this post


Link to post
Share on other sites
2 hours ago, Janio Aguiar said:

Essa construção nao eh permitida no mysql. Dando erro aqui

Na verdade, aceita sim

INSERT INTO a20dce (RECNO)
SELECT MAX(RECNO)+1 FROM a20dce

Share this post


Link to post
Share on other sites
1 hour ago, Mello Junior said:

Amigo,

Utilize o que normalmente o Banco oferece, a função de agregação serve sim, mas o script não pode ser apenas para saber qual o maior número único. Em postgres temos um tipo especial de objeto denominado sequencia a qual pode ser manipulada simplesmente dizendo para o "DEFAULT" da coluna da tabela que pegue o próximo valor da sequência. Agora caso não seja PostgreSQL, atente para alguns detalhes. 

1. Isolar a tabela

2. Pegar o maior valor (normalmente em Postgres você faz isso com apenas uma sentença SELECT adicionando uma cláusula FOR UPDATE;

3. Inserir na tabela o Registro pretendido já utilizando o max + 1 obtido na sentença anterior

4. Liberar o isolamento (Commit da vida)

Att

 

Excelente, Melo

Irei testar todas as soluções

Share this post


Link to post
Share on other sites

se pegar o id no próprio insert acredito não ser necessário fazer bloqueio da tabela, isso pode na verdade ser ruim, pois pode segurar outros processos que acessam essa tabela, como falou que é MySQL dependendo da opção nem vai ter suporte a transação
não sei exatamente no MySQL mas por exemplo no sqlserver quando existe uma escrita o próprio banco cria uma trava, você só teria problema se usar transação, ai sim vai acabar duplicando seu id

Share this post


Link to post
Share on other sites

Jânio como vc veio do xharbour, veja o código abaixo, aqui eu utiliza uma tabela a parte apenas para o controle desses numero, que no meu caso tb não pode ser o campo ID, trabalho com mysql.

function fSequencia_generica(cAlias,cCodcli,cCampo,nTamanho,nTabela)

  local nSqlro1,aCad,aCad1,aca1,cSql1

  local aSql:={}

  default cCampo:='codigo'

  default nTamanho:=7

  aCad:="SELECT "+ccampo

  aCad+=" FROM "+cAlias

  aCad+=" WHERE uso1='N'"

  aCad+=" AND uso2='N'"

  aCad+=" AND tabela ='"+nTabela+"'"

  SqlExecute("commit")	 

  aSql:=SqlArray(aCad)

  if len(aSql)=0

     aCad:="SELECT "+ccampo

     aCad+=" FROM "+cAlias

     aCad+=" WHERE tabela ='"+nTabela+"'"

     aCad+=" ORDER BY "+cCampo+" DESC LIMIT 1 "

     SqlExecute("commit")	 

     aSql:=SqlArray(aCad)

     if SQLErrorNO() > 0

        MsgStop("Numero do Erro SQL.: " + STR(SQLErrorNO(),10,0)+CHR(13)+CHR(13)+SQLErrorMsg()+" "+'cBanco',"sistema")     

        return(.F.)

     endif   

     if LEN(aSql)==0

        nSqlro1:=STR(1)

        nSqlrow=str(Val(nSqlro1))

        cCodcli=(StrZero(Val(nSqlro1),nTamanho))

     else

        nSqlro1:=aSql[01,01]

        nSqlrow=str(Val(nSqlro1)+1)

        cCodcli=Strzero((Val(nSqlro1)+1),nTamanho)

     endif

     SQLBeginTrans()

     cSql1:=" UNLOCK TABLES"

     sqlexecute(cSql1)

     SQLEndTrans()



     SQLBeginTrans()

     cSql:="INSERT INTO "+cAlias+" ( tabela,codigo,uso1,uso2 )" 

     cSql+="VALUES( "+any2sql(nTabela)+" , "+any2sql(cCodcli)+" , "+any2sql('S')+" , "+any2sql('N')+" )"

     SqlExecute(cSql)

     if SQLErrorNO() > 0 

        fMensagem() 

        return(.f.) 

     endif 

     SqlExecute("commit")	 

     SQLRollBack()

     SQLEndTrans()

     SQLRefresh() 

  else

     cCodcli=(strzero(val(aSql[1,1]),nTamanho))

     SQLBeginTrans()

     cSql:="UPDATE "+cAlias+" SET"

     cSql+="   uso1="+Any2Sql('S')+" "

     cSql+="  WHERE codigo="+Any2Sql(cCodcli)+" "

	  cSql+="  AND tabela='"+nTabela+"'"

     sqlexecute(cSql)

     if SQLErrorNO() > 0 

        fMensagem() 

        return(.f.) 

     endif 

     SQLExecute("commit")

     SQLRollBack() 

     SQLEndTrans() 

     SQLRefresh()

  endif

return(NIL)

exemplo de uma aplicação que uso isso, cadastro de produto, preciso do código do produto antes de incluir ele, qdo clico em novo ele pega o numero e deixa o campo USO1 com S, caso eles sair sem gravar faço um UPDATE deixando este campo USO1 como N, aonde sera aproveitado para o próximo cadastro. 

1 person likes this

Share this post


Link to post
Share on other sites

Bom dia,

Pessoal, peguei um mucadim de cada dica e fiz uma função. Nos testes passou ok sem repetição. Coloquei no cliente e estou esperando alguns dias pra ver se funcionou mesmo. Funcionando, eu posto aqui a função

Janio

1 person likes this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now