- Este tópico contém 16 respostas, 5 vozes e foi atualizado pela última vez 16 anos, 9 meses atrás por
facc.
-
AutorPosts
-
9 de junho de 2009 às 3:32 pm #87224
facc
ParticipanteBom dia a todos.
Estou com um problemão nas mãos.
É o seguinte, a empresa que trabalho, comprou 100.000 números para sorteio de premios junto com a Caixa Federal. Esses números são únicos (não se repetem), então criei uma base e exportei esses numeros para uma tabela em um servidor Central, onde todas as filiais (67 para ser exato) acessam essa tabela através de uma procedure para reservar essa numeração. Vou tentar explicar melhor esse procedimento.
1 – A loja acessa essa tabela (através de uma procedure) dizendo que filial é e a quantidade de numeros que ela está vendendo retornando os números “gerados”.
2 – Essa procedure se encarrega de reservar esses números e retorna-los para o sistema PDV (onde vendeu), porém em alguns casos esses números acabam se repetindo, o que não pode acontecer.
3 – A procedure grava o número da loja, PDV que vendeu e a hora da venda e altera uma flag para o número que o SELECT achou livre.
Abaixo segue a procedure que faz essa reserva.
CREATE OR REPLACE PROCEDURE CYBELAR_RES_NRSORTE(P_LOJA IN varchar2,
P_PDV IN VARCHAR2,
P_QTD IN NUMBER,
P_NROSORTE OUT varchar2,
P_CONFIRMA OUT NUMBER) is
/***********************************************************
* PROCEDURE : CYBELAR_RES_NRSORTE *
* OBJETIVO : RESERVAR O NRSORTE P/ A LOJA E "ESCONDER" *
DAS DEMAIS LOJAS E OUTRAS RESERVAS *
* CRIACAO : 08/05/2009 *
* VERSAO : 1.0 *
* AUTOR : FABIO A. CAMPOS CRUZ - fabioc@cybelar.com.br *
***********************************************************/pragma autonomous_transaction;
retorno_nrsorte varchar2(5);
ListaNrsorte varchar2(1000);
RETORNOCONFIRMA NUMBER;
erro_int varchar2(1000);
vc_dir_log varchar2(100);
vc_id_log varchar2(7) := 'CYBELAR';
vc_arq_log varchar2(15) := 'NRSORTE.LOG';
vc_nome_pgm varchar2(50) := 'PDV_NRSORTE';
vu_file utl_file.file_type;VN_QTREG NUMBER := 0;
/*cursor cur_nrosorte is
/*SELECT NROSORTE INTO VG_NRSORTE
FROM CYBELAR_NROSORTE
WHERE FLGUSO = 0 AND (LOJA IS NULL OR LOJA = '')
AND ROWNUM < P_QTD + 1 FOR UPDATE OF LOJA NOWAIT; select nrosorte from cybelar_nrosorte where flguso = 0 AND (LOJA IS NULL OR LOJA = '') and rownum < P_QTD + 1; res cur_nrosorte%rowtype; */ begin BEGIN SELECT PINT_NM_DIRETORIO_LOG INTO VC_DIR_LOG FROM GEMCO_PARAMETRO_INTERFACE PINT, GEMCO_SISTEMA SIST WHERE PINT.PINT_CD_SISTEMA = SIST.SIST_CD_SISTEMA AND SIST.SIST_DS_SISTEMA = vc_nome_pgm; EXCEPTION -- SE NAO EXISTIR INFORMAR O DIRETORIO ONDE DEVERA SER -- GERADO O LOG DE OCORRENCIAS WHEN NO_DATA_FOUND THEN VC_DIR_LOG := '/integra/Log'; WHEN TOO_MANY_ROWS THEN VC_DIR_LOG := '/integra/Log'; WHEN OTHERS THEN VC_DIR_LOG := '/integra/Log'; END; begin vu_file := utl_file.fopen(vc_dir_log, vc_arq_log, 'r'); utl_file.fclose(vu_file); exception when others then sp_int_gemco_gera_log('INICIO DO LOG', vc_dir_log, vc_id_log, vc_arq_log, sysdate, null, vc_nome_pgm, 0, 0, 0); end; sp_int_gemco_gera_log('GERA LOG', vc_dir_log, vc_id_log, vc_arq_log, sysdate, 'INICIO DO LOG', vc_nome_pgm, 0, 0, 0); --open cur_nrosorte; FOR QTD IN 1 .. P_QTD loop --fetch cur_nrosorte --into res; /*if cur_nrosorte%notfound then ERRO_INT := NULL; ERRO_INT := 'ERRO AO RESERVAR O NRSORTE PARA A LOJA ' || P_LOJA || '.' || sqlerrm; RETORNOCONFIRMA := 1; ROLLBACK; exit; ELSE*/ UPDATE CYBELAR_NROSORTE SET FLGUSO = 1, LOJA = P_LOJA, PDV_CAIXA = P_PDV, DATANRSORTE = SYSDATE WHERE NROSORTE = (SELECT NROSORTE FROM CYBELAR_NROSORTE WHERE FLGUSO = 0 AND (LOJA IS NULL OR LOJA = '')) RETURNING NROSORTE INTO retorno_nrsorte; COMMIT; ListaNrsorte := retorno_nrsorte || '|' || ListaNrsorte; RETORNOCONFIRMA := 0; --end if; VN_QTREG := VN_QTREG + 1; end loop; IF VN_QTREG = 0 THEN ERRO_INT := 'ERRO AO RESERVAR O NRSORTE (' || P_NROSORTE || ') PARA A LOJA ' || P_LOJA || '.'; RETORNOCONFIRMA := 1; ELSE ERRO_INT := 'RESERVADO COM SUCESSO (' || ListaNrsorte || '). LOJA ' || P_LOJA; RETORNOCONFIRMA := 0; END IF; sp_int_gemco_gera_log('GERA LOG', vc_dir_log, vc_id_log, vc_arq_log, sysdate, erro_int, vc_nome_pgm, 0, 0, 0); sp_int_gemco_gera_log('GERA LOG', vc_dir_log, vc_id_log, vc_arq_log, sysdate, 'FIM DO LOG', vc_nome_pgm, 0, 0, 0); sp_int_gemco_gera_log('FIM LOG', vc_dir_log, vc_id_log, vc_arq_log, sysdate, null, vc_nome_pgm, 0, 0, 0); --close cur_nrosorte; COMMIT; P_CONFIRMA := RETORNOCONFIRMA; P_NROSORTE := ListaNrsorte; end cybelar_res_nrsorte;
Lembrando que as 67 lojas podem acessar ao mesmo tempo essa procedure.9 de junho de 2009 às 3:41 pm #87225lipcurl
ParticipanteBom dia Meu amigo,
Não entendi a sua dúvida ainda.
9 de junho de 2009 às 3:52 pm #87226facc
ParticipanteDeixa eu tentar explicar melhor
Tenho uma tabela com 100.000 registros e preciso “bloquear” uma certa quantidade (a loja irá dizer) e esconder esses numeros das demais lojas.
9 de junho de 2009 às 4:09 pm #87229Regis Araujo
ParticipanteOla Facc, bom dia!
Bom, pelo que eu entendi.. você precisa separar registros para determinadas lojas..
Bom, creio que a maneira mais fácil é você criar um campo em sua tabela para fazer o controle destes registros, tipo, criar um PT_LOJA e neste campo você informa a qual loja ele pertence e no seu select de consulta você pode colocar como clausula onde o campo PT_LOJA for nulo e/ou igual ao valor determinado para cada loja.
Espero que isto lhe ajude.
Abraços..
9 de junho de 2009 às 4:11 pm #87230lipcurl
ParticipanteExato.
Também concordo.
9 de junho de 2009 às 4:15 pm #87231facc
Participante[quote=”Thunder_Catz”:3uefnx5d]Ola Facc, bom dia!
Bom, pelo que eu entendi.. você precisa separar registros para determinadas lojas..
Bom, creio que a maneira mais fácil é você criar um campo em sua tabela para fazer o controle destes registros, tipo, criar um PT_LOJA e neste campo você informa a qual loja ele pertence e no seu select de consulta você pode colocar como clausula onde o campo PT_LOJA for nulo e/ou igual ao valor determinado para cada loja.
Espero que isto lhe ajude.
Abraços..[/quote]
Já pensei nessa possibilidade, mas caso a empresa abra uma nova filial, isso se torna inviavel.
Na tabela onde se encontram esses 100.000 registros, possui um campos chamado Loja (grava a filial) e flguso (para dizer se está “bloqueado” ou não) mas o que dá impressão que o select está ignorando em alguns casos esses campos.9 de junho de 2009 às 4:24 pm #87232eversonpiza
ParticipanteQuando uma loja reserva uma quantidade de números, eles tem que ser sequenciais?
Se não, minha sugestão é a seguinte.1-Fazer o lock de apenas um registro.
“select * from tabela where PT_LOJA is null and rownum = 1 FOR UPDATE NOWAIT”.
2-Fazer o update colocando o valor em PT_LOJA
3-Quardar o número reservado
4-Ficar em um loop até terminar a quantidade solicitada
5-dar commit.
6-Retornar todos os reservados.No passo 1, vc faria um tratamento da exception, e quando o registro estiver locado, vc parte para o próximo, e assim por diante.
9 de junho de 2009 às 4:29 pm #87233Regis Araujo
ParticipanteOpa..
Agora que percebi que você já faz isto que lhe falei..
E entendi o que ocorre. Isto ocorre devido a você não travar o registro antes de realizar o UPDATE, você da um select para retornar o valor, mas não da um LOCK neste valor, coloque junto do seu select um “for update skip locked” ou crie uma função que locke o registro e trabalhe com este registro, assim você terá a certeza que ninguem irá acessar este registro atraves de outra sessão..
Mais ou menos assim a função..
FUNCTION FC_IsLocked(pVALOR IN NUMBER) RETURN PLS_INTEGER IS
iResult PLS_INTEGER := 0;
BEGIN
SELECT VALOR INTO iResult FROM TABELA p WHERE p.VALOR= pVALOR FOR UPDATE NOWAIT;RETURN(iResult);EXCEPTION
WHEN OTHERS THEN
RETURN(iResult);
END FC_IsLocked;Ai em um bloco da sua procedure.. você ficaria mais ou menos desta maneira… Tem como ajustar melhor isto.. fiz como gambiarra.. mas acho que dá para vc entender a lógica…
CURSOR cDados IS SELECT NROSORTE FROM CYBELAR_NROSORTE WHERE FLGUSO = 0 AND (LOJA IS NULL OR LOJA = '') AND ROWNUM 0 THEN UPDATE CYBELAR_NROSORTE SET FLGUSO = 1, LOJA = P_LOJA, PDV_CAIXA = P_PDV, DATANRSORTE = SYSDATE WHERE NROSORTE = cDados.NROSORTE; RETURNING NROSORTE INTO retorno_nrsorte;END LOOP;
Abraços..
9 de junho de 2009 às 5:14 pm #87235facc
ParticipanteFiz isso que vc me sugeriu, mas não está retornando a qntdd solicitada
9 de junho de 2009 às 5:39 pm #87241Marcio68Almeida
Participante[quote=”facc”:mwact5q4]Fiz isso que vc me sugeriu, mas não está retornando a qntdd solicitada[/quote]
Se não está retornando a quantidade solicitada é por que todos estão fazendo o mesmo processo e não há número suficientes para todos…
Quantos números está reservando para cada loja ???9 de junho de 2009 às 5:45 pm #87242facc
Participantenumeros á sim, a tabela tem 100.000 registros.
em testes aqui (2 computadores) ela não me retornou a quantidade solicitada, nos testes fizemos pedimos 5 em um computador e mais 5 no outro.
No cupom ela repetiu a numeração.Mas já estamos num caminho pra a resolução final.
9 de junho de 2009 às 5:55 pm #87243facc
ParticipanteEstou enviando a procedure para a analise
CREATE OR REPLACE PROCEDURE CYBELAR_RES_NRSORTE(P_LOJA IN varchar2,
P_PDV IN VARCHAR2,
P_QTD IN NUMBER,
P_NROSORTE OUT varchar2,
P_CONFIRMA OUT NUMBER) is
/***********************************************************
* PROCEDURE : CYBELAR_RES_NRSORTE *
* OBJETIVO : RESERVAR O NRSORTE P/ A LOJA E "ESCONDER" *
DAS DEMAIS LOJAS E OUTRAS RESERVAS *
* CRIACAO : 08/05/2009 *
* VERSAO : 1.0 *
* AUTOR : FABIO A. CAMPOS CRUZ - fabioc@cybelar.com.br *
***********************************************************/pragma autonomous_transaction;
retorno_nrsorte varchar2(5);
ListaNrsorte varchar2(1000);
RETORNOCONFIRMA NUMBER;
erro_int varchar2(1000);
vc_dir_log varchar2(100);
vc_id_log varchar2(7) := 'CYBELAR';
vc_arq_log varchar2(60) := 'NRSORTE_' || to_char(sysdate, 'DDMMYY') ||
'.LOG';
vc_nome_pgm varchar2(50) := 'PDV_NRSORTE';
vu_file utl_file.file_type;VN_QTREG NUMBER := 0;
cursor cur_nrosorte is
--cursor pega numeros "livres"
select nrosorte
from cybelar_nrosorte
where flguso = 0
AND (LOJA IS NULL OR LOJA = '')
and rownum = P_QTD + 1;res cur_nrosorte%rowtype;
begin
BEGIN
SELECT PINT_NM_DIRETORIO_LOG
INTO VC_DIR_LOG
FROM GEMCO_PARAMETRO_INTERFACE PINT, GEMCO_SISTEMA SIST
WHERE PINT.PINT_CD_SISTEMA = SIST.SIST_CD_SISTEMA
AND SIST.SIST_DS_SISTEMA = vc_nome_pgm;
EXCEPTION
-- SE NAO EXISTIR INFORMAR O DIRETORIO ONDE DEVERA SER
-- GERADO O LOG DE OCORRENCIAS
WHEN NO_DATA_FOUND THEN
VC_DIR_LOG := '/integra/Log';
WHEN TOO_MANY_ROWS THEN
VC_DIR_LOG := '/integra/Log';
WHEN OTHERS THEN
VC_DIR_LOG := '/integra/Log';
END;begin
vu_file := utl_file.fopen(vc_dir_log, vc_arq_log, 'r');
utl_file.fclose(vu_file);
exception
when others then
sp_int_gemco_gera_log('INICIO DO LOG',
vc_dir_log,
vc_id_log,
vc_arq_log,
sysdate,
null,
vc_nome_pgm,
0,
0,
0);
end;FOR rNrSorte IN cur_nrosorte loop
if CYBELAR_LCK_REGISTRO(rNrSorte.nrosorte) > 0 then UPDATE CYBELAR_NROSORTE SET FLGUSO = 1, LOJA = P_LOJA, PDV_CAIXA = P_PDV, DATANRSORTE = SYSDATE WHERE NROSORTE = rNrSorte.nrosorte RETURNING NROSORTE INTO retorno_nrsorte; COMMIT; ListaNrsorte := retorno_nrsorte || '|' || ListaNrsorte; RETORNOCONFIRMA := 0; end if; VN_QTREG := VN_QTREG + 1;end loop;
close cur_nrosorte;IF VN_QTREG = 0 THEN
ERRO_INT := 'ERRO AO RESERVAR O NRSORTE (' || P_NROSORTE ||
') PARA A LOJA ' || P_LOJA || '.';
RETORNOCONFIRMA := 1;
ELSE
ERRO_INT := 'RESERVADO COM SUCESSO (' || ListaNrsorte ||
'). LOJA ' || P_LOJA;
RETORNOCONFIRMA := 0;
END IF;sp_int_gemco_gera_log('GERA LOG',
vc_dir_log,
vc_id_log,
vc_arq_log,
sysdate,
erro_int,
vc_nome_pgm,
0,
0,
0);sp_int_gemco_gera_log('FIM LOG',
vc_dir_log,
vc_id_log,
vc_arq_log,
sysdate,
null,
vc_nome_pgm,
0,
0,
0);
COMMIT;P_CONFIRMA := RETORNOCONFIRMA;
P_NROSORTE := ListaNrsorte;end cybelar_res_nrsorte;
9 de junho de 2009 às 7:11 pm #87249facc
Participanteuma coisa que descobri que se eu solicitar em um computador 4 numeros e em outro eu solicitar 3, no qual soluciou 4, este recebe apenas 3 dos 4 e no outro apenas 1 numero, totalizando os 4 solicitados no primeiro computador.
Como posso rodar essa procedure “concorrente”, ou seja independente de quem solicitou primeiro.
9 de junho de 2009 às 7:21 pm #87250facc
Participante[quote=”eversonpiza”:2pylfw96]Quando uma loja reserva uma quantidade de números, eles tem que ser sequenciais?
Se não, minha sugestão é a seguinte.1-Fazer o lock de apenas um registro.
“select * from tabela where PT_LOJA is null and rownum = 1 FOR UPDATE NOWAIT”.
2-Fazer o update colocando o valor em PT_LOJA
3-Quardar o número reservado
4-Ficar em um loop até terminar a quantidade solicitada
5-dar commit.
6-Retornar todos os reservados.No passo 1, vc faria um tratamento da exception, e quando o registro estiver locado, vc parte para o próximo, e assim por diante.[/quote]
vc poderia me dar um exemplo pratico?
9 de junho de 2009 às 8:38 pm #87251eversonpiza
ParticipanteNão cheguei a testar, mas seria algo mais ou menos assim:
tabela:
TAB_NUMEROS
(NUMERO NUMBER
LOJA NUMBER);PROCEDURE PRC_RESERVA_NUMERO(iQtd NUMBER, iLoja NUMBER, iLista out varchar2) IS
lock_error exception;
pragma exception_init(lock_error,-54);vLista varchar2(4000);
vNumero number;BEGIN
FOR C IN 1.. iQtd LOOP
BEGIN
SELECT T.NUMERO INTO vNumero
FROM TAB_NUMEROS
WHERE LOJA IS NULL
AND ROWNUM <= 1
FOR UPDATE NOWAIT;
vLista := vLista || '|' || vNumero;
UPDATE TAB_NUMEROS SET LOJA = iLoja WHERE NUMERO = vNumero;
commit;
EXCEPTION
WHEN lock_error then null;
END;
END LOOP;
END;
/
Neste exemplo ele pega um por um e já dá o commit, se der algum erro inesperado no meio vc vai perder os números já selecionados, outra forma é já selecionar todas de uma vez.
-
AutorPosts
- Você deve fazer login para responder a este tópico.