Usuário com melhor resposta
Optimização de procedure

Pergunta
-
Olá a todos,
estou realizando um procedure de carga.
O problema que estou enfrentando e que a procedure está levando muito tempo para executar. Gostaria de pedir ajuda para que os senhores pudessem me direcionar onde devo alterar o sql ou se existe metodos mais eficazes.
Basicamente ela procura uma quantidade de lotes especificos e agrupa em uma temp, depois preciso agrupar os lotes por data para realizar o cálculo.
Utilizo três funções:
- ano_referencia que procura o ultimo dia util do mes anterior do ano anterior
-data_referencia que procura o ultimo dia util do mes anterior
-data_seguinte que adiciona uma data util
- Editado Rafael R Santos terça-feira, 11 de dezembro de 2012 21:21 A página estava ficando muito grande
Respostas
-
Rafael,
Teoricamente não é para ficar mais lento, ao contrário você estaria dividindo a carga de trabalho em processamentos paralelos.
Em relação as tabelas é possível sim criar uma temporária a outras procedures ou sessões utilizarem, mas para isso terá que ser uma table temporária global e não local.
Vale ressaltar que toda tabela temporária global vai existir enquanto a conexão do usuário na instância do SQL Server estiver em uso e ativa.
Pedro Antonio Galvão Junior [MVP | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados | SorBR.Net | Professor Universitário | MSIT.com]
- Marcado como Resposta Rafael R Santos segunda-feira, 10 de dezembro de 2012 18:34
-
Olá Rafael,
A primeira coisa que você deve fazer é eliminar o uso de cursores na procedure.
Abraço
MCP – MCTS – MCITP – MCT SQL Server ADM / BI / Dev Sharepoint IBM Optim Certification http://demetriosilva.wordpress.com/
- Marcado como Resposta Rafael R Santos terça-feira, 11 de dezembro de 2012 16:10
Todas as Respostas
-
Rafael,
Realmente a Procedure é bem extensa o que acaba tornando impossível o entendimento.
Uma pergunta, você já tentou dividir esta procedure em outras pequenas procedures e fazer a chamada uma dentro da outra?
Pedro Antonio Galvão Junior [MVP | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados | SorBR.Net | Professor Universitário | MSIT.com]
-
Junior, pensei sobre o assunto, mas será que isso não deixaria a procedure mais lenta, uma vez que o código tem que chamar outro código.
Mais uma pergunta, se eu dividisse essa procedure em pequenas procedures. Seria possivel que uma procedure criasse tabelas temporarias e as demais utilizassem essas tabelas???
Abraços, obrigado por me ajudar.
-
Rafael,
Teoricamente não é para ficar mais lento, ao contrário você estaria dividindo a carga de trabalho em processamentos paralelos.
Em relação as tabelas é possível sim criar uma temporária a outras procedures ou sessões utilizarem, mas para isso terá que ser uma table temporária global e não local.
Vale ressaltar que toda tabela temporária global vai existir enquanto a conexão do usuário na instância do SQL Server estiver em uso e ativa.
Pedro Antonio Galvão Junior [MVP | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados | SorBR.Net | Professor Universitário | MSIT.com]
- Marcado como Resposta Rafael R Santos segunda-feira, 10 de dezembro de 2012 18:34
-
-
Olá Rafael,
A primeira coisa que você deve fazer é eliminar o uso de cursores na procedure.
Abraço
MCP – MCTS – MCITP – MCT SQL Server ADM / BI / Dev Sharepoint IBM Optim Certification http://demetriosilva.wordpress.com/
- Marcado como Resposta Rafael R Santos terça-feira, 11 de dezembro de 2012 16:10
-
Demétrio,
realemnte você tem razão, eu consegui eliminar um cursor, o C_GERAL, mas o que corre os planos dentro de cada #TEMP_AUX...BD,CV OU ADM não tem como retirar, pois os registros dentro dessas temps variam de acordo com o dia e o plano.
Já realizei um alteração na procedure, coloquei um loop WHILE no lugar do cursor C_GERAL e troquei alguns selects por set, consegui até agora uma melhoria de aproximadamente 2 minutos e 30 segundos por mês corrido.
O próximo passo será criar procedures que chamem outras procedures, ou seja irei tentar quebrar essa procedure monstro em procedures menores como o Junior Galvão aconselhou.
Colocarei os resultados obtidos aqui após a execução.
Agradeço a todos que opinaram a fim de colaborar com crescimento de conhecimento do forum.
Abraços, até breve
-
-
Caros,
realizei a mudança, retirei todos os cursores e quebrei a procedure em procedures menores, mas houve um acrescimo de tempo de 3 segundos.
CREATE PROCEDURE carga_rentabilidade_grupo AS BEGIN --Cria tabela temporária que irá armazenar todos atributos necessários das tabelas --STAGE_RENTABILIDADE_RV; STAGE_RENTABILIDADE_RF; STAGE_RENTABILIDADE_CE; STAGE_RENTABILIDADE_IM CREATE TABLE ##TEMP_GRUPO_PAPEIS( ID INT IDENTITY(1,1), SK_DIM_PLANO INT, SK_DT_REF_SALDO INT, COD_EMP_CT INT, TIPO CHAR(2)) CREATE TABLE ##TEMP_AUX_BD( SK_DIM_PLANO INT, SK_DT_REF_SALDO INT, DATA DATE, DATA_AUX DATE, COD_EMP_CT INT, TIPO CHAR(2)) CREATE TABLE ##TEMP_AUX_CV( SK_DIM_PLANO INT, SK_DT_REF_SALDO INT, DATA DATE, DATA_AUX DATE, COD_EMP_CT INT, TIPO CHAR(2)) CREATE TABLE ##TEMP_AUX_ADM( SK_DIM_PLANO INT, SK_DT_REF_SALDO INT, DATA DATE, DATA_AUX DATE, COD_EMP_CT INT, TIPO CHAR(2)) --Popula tabela TEMP_RENDIMENTO INSERT INTO ##TEMP_GRUPO_PAPEIS SELECT SK_DIM_PLANO,SK_DT_REF_SALDO,NULL,'RV' FROM STAGE_RENTABILIDADE_RV INSERT INTO ##TEMP_GRUPO_PAPEIS SELECT SK_DIM_PLANO,DT_REF_SLD,NULL,'RF' FROM STAGE_RENTABILIDADE_RF INSERT INTO ##TEMP_GRUPO_PAPEIS SELECT CASE COD_EMP_CT WHEN 1 THEN 3 WHEN 3 THEN 2 END,DT_REF_SALDO,COD_EMP_CT,'CE' FROM STAGE_RENTABILIDADE_CE INSERT INTO ##TEMP_GRUPO_PAPEIS SELECT SK_DIM_PLANO,DT_REF_SLD_IM,NULL,'IM' FROM STAGE_RENTABILIDADE_IM DECLARE --Variaveis gerais @DATA DATE, @SK_DT_REF_SALDO INT, @MAX_DT_REF INT, @AUX_DT_REF_SALDO INT, @CONT_LINHAS INT, @COMPARA_DATA DATE, @DATA_REFERENCIA DATE --Delimita a data de pesquisa SELECT @SK_DT_REF_SALDO = MIN(SK_DT_REF_SALDO), @MAX_DT_REF = MAX(SK_DT_REF_SALDO) FROM ##TEMP_GRUPO_PAPEIS TEMP INNER JOIN DIM_TEMPO DT ON TEMP.SK_DT_REF_SALDO = DT.ID_TEMPO WHERE CONVERT(DATE,DT.DATA) BETWEEN '2012/10/01' AND '2012/10/31' --Referência do loop uma vez que este numero representa a primeira data de pápeis em 2009/11/03 que serve como parametro para --os papeis do mês 11 do ano de 2009 SET @AUX_DT_REF_SALDO = 7247 --Este loop é responsável por percorrer a ##TEMP_GRUPO_PAPEIS que possui todos os papeis dos planos WHILE @SK_DT_REF_SALDO <= @MAX_DT_REF --LOOP PRINCIPAL BEGIN IF @AUX_DT_REF_SALDO != @SK_DT_REF_SALDO BEGIN SELECT @DATA = CONVERT(DATE,DATA) FROM DIM_TEMPO WHERE ID_TEMPO = @SK_DT_REF_SALDO SELECT @DATA_REFERENCIA = DBO.DATA_REFERENCIA(@DATA) --Insere os papeis nas respectivas tabelas temporarias de seus planos INSERT INTO ##TEMP_AUX_BD SELECT SK_DIM_PLANO,SK_DT_REF_SALDO,@DATA,@DATA_REFERENCIA,COD_EMP_CT,TIPO FROM ##TEMP_GRUPO_PAPEIS WHERE SK_DT_REF_SALDO = @SK_DT_REF_SALDO AND SK_DIM_PLANO = 3 GROUP BY SK_DT_REF_SALDO, SK_DIM_PLANO, COD_EMP_CT, TIPO ORDER BY TIPO INSERT INTO ##TEMP_AUX_CV SELECT SK_DIM_PLANO,SK_DT_REF_SALDO,@DATA,@DATA_REFERENCIA,COD_EMP_CT,TIPO FROM ##TEMP_GRUPO_PAPEIS WHERE SK_DT_REF_SALDO = @SK_DT_REF_SALDO AND SK_DIM_PLANO = 2 GROUP BY SK_DT_REF_SALDO, SK_DIM_PLANO, COD_EMP_CT, TIPO ORDER BY TIPO INSERT INTO ##TEMP_AUX_ADM SELECT SK_DIM_PLANO,SK_DT_REF_SALDO,@DATA,@DATA_REFERENCIA,COD_EMP_CT,TIPO FROM ##TEMP_GRUPO_PAPEIS WHERE SK_DT_REF_SALDO = @SK_DT_REF_SALDO AND SK_DIM_PLANO = 1 GROUP BY SK_DT_REF_SALDO, SK_DIM_PLANO, COD_EMP_CT, TIPO ORDER BY TIPO --Verifica se existem linhas para ##TEMP_AUX_BD SELECT @CONT_LINHAS = COUNT(*) FROM ##TEMP_AUX_BD IF @CONT_LINHAS != 0 BEGIN EXEC DBO.BD_RENTABILIDADE_GRUPO @SK_DT_REF_SALDO, @DATA, @DATA_REFERENCIA; END SELECT @CONT_LINHAS = COUNT(*) FROM ##TEMP_AUX_CV IF @CONT_LINHAS != 0 BEGIN EXEC DBO.CV_RENTABILIDADE_GRUPO @SK_DT_REF_SALDO, @DATA, @DATA_REFERENCIA; END SELECT @CONT_LINHAS = COUNT(*) FROM ##TEMP_AUX_ADM IF @CONT_LINHAS != 0 BEGIN EXEC DBO.ADM_RENTABILIDADE_GRUPO @SK_DT_REF_SALDO, @DATA, @DATA_REFERENCIA; END SELECT @AUX_DT_REF_SALDO = @SK_DT_REF_SALDO TRUNCATE TABLE ##TEMP_AUX_BD TRUNCATE TABLE ##TEMP_AUX_CV TRUNCATE TABLE ##TEMP_AUX_ADM END -- Fim do IF @AUX_DT_REF_SALDO != @SK_DT_REF_SALDO SELECT @DATA = CONVERT(DATE,DATA) FROM DIM_TEMPO WHERE ID_TEMPO = @SK_DT_REF_SALDO SELECT @DATA = DBO.DATA_SEGUINTE(@DATA) SELECT @SK_DT_REF_SALDO = ID_TEMPO FROM DIM_TEMPO WHERE CONVERT(DATE,DATA) = @DATA END END ------------------------------------------------------- ----------Procedure DBO.BD_RENTABILIDADE_GRUPO--------- ------------------------------------------------------- CREATE PROCEDURE bd_rentabilidade_grupo @SK_DT_REF_SALDO INT, @DATA DATE, @DATA_REFERENCIA DATE, AS BEGIN DECLARE @COMPARA_DATA DATE, @QNT_LINHAS INT, @TIPO_GERAL CHAR(2), --PAPEIS_CE @DT_REF_COTA_CE DATE, @VL_AMORTIZACAO_CE MONEY, @VL_SALDO_ATUAL_CE MONEY, @VL_CONCESSAO_CE MONEY, --PAPEIS_IM @VL_OPER_COTA_IM MONEY, @PATRIMONIO_IM MONEY, --PAPEIS_RF @VL_OPER_COTA_RF MONEY, @PATRIMONIO_RF MONEY, --PAPEIS_RV @VL_OPER_COTA_RV MONEY, @VL_DIVID_PREV_RV MONEY, @PATRIMONIO_RV MONEY, --Totalizador plano DB @VL_OPER_COTA_TOTAL MONEY, @VL_DIVID_PREV_TOTAL MONEY, @PATRIMONIO_TOTAL MONEY, @QTD_COTA_TOTAL MONEY, @VL_COTA_TOTAL MONEY, @AUX_QNT_COTA_ANTERIOR_TOTAL MONEY, @RENTABILIDADE_MES MONEY --Inicializa as variáveis SELECT @VL_AMORTIZACAO_CE = 0, @VL_SALDO_ATUAL_CE = 0, @VL_CONCESSAO_CE = 0, @VL_OPER_COTA_IM = 0, @PATRIMONIO_IM = 0, @VL_OPER_COTA_RF = 0, @PATRIMONIO_RF = 0, @VL_OPER_COTA_RV = 0, @VL_DIVID_PREV_RV = 0, @PATRIMONIO_RV = 0, @VL_OPER_COTA_TOTAL = 0, @VL_DIVID_PREV_TOTAL = 0, @PATRIMONIO_TOTAL = 0, @QTD_COTA_TOTAL = 0, @VL_COTA_TOTAL = 0, @AUX_QNT_COTA_ANTERIOR_TOTAL = 0, @RENTABILIDADE_MES = 0 --@COMPARA_DATA é a referência da data @DATA do papel SELECT @COMPARA_DATA = @DATA_REFERENCIA WHILE @COMPARA_DATA <= @DATA BEGIN SELECT @QNT_LINHAS = COUNT(SK_DIM_PLANO) FROM ##TEMP_AUX_BD WHILE @QNT_LINHAS >= 1 --Loop da ##TEMP_AUX_BD BEGIN SELECT @TIPO_GERAL = X.TIPO FROM (SELECT ROW_NUMBER() OVER(ORDER BY TIPO) AS LINHA,TIPO FROM ##TEMP_AUX_BD) AS X WHERE X.LINHA = @QNT_LINHAS --Pápeis do EMPRÉSTIMO IF @TIPO_GERAL = 'CE' BEGIN SELECT @DT_REF_COTA_CE = CONVERT(DATE,SD.DT_REF), @VL_AMORTIZACAO_CE = SUM(VL_AMORTIZACAO), @VL_SALDO_ATUAL_CE = SUM(VL_SALDO_ATUAL) FROM DBINTECH.DBO.CE_SALDO_DIARIO_CONTRATO SD, DBINTECH.DBO.CE_CONTRATOS CE WHERE SD.CD_FUNDACAO = '01' AND CONVERT(DATE,SD.DT_REF) = @COMPARA_DATA AND SD.SEQ_PREST = (SELECT MAX(SD1.SEQ_PREST) FROM DBINTECH.DBO.CE_SALDO_DIARIO_CONTRATO SD1 WHERE SD1.CD_FUNDACAO = SD.CD_FUNDACAO AND SD1.ANO_CONTRATO = SD.ANO_CONTRATO AND SD1.NUM_CONTRATO = SD.NUM_CONTRATO AND SD1.DT_REF = SD.DT_REF) AND CE.CD_FUNDACAO = SD.CD_FUNDACAO AND CE.ANO_CONTRATO = SD.ANO_CONTRATO AND CE.NUM_CONTRATO = SD.NUM_CONTRATO AND CE.CD_FUNDACAO = SD.CD_FUNDACAO AND CE.ANO_CONTRATO = SD.ANO_CONTRATO AND CE.NUM_CONTRATO = SD.NUM_CONTRATO AND ((CE.CD_SITUACAO = 3 AND CE.DT_CREDITO <= SD.DT_REF) OR (CE.CD_SITUACAO = 5 AND CE.DT_QUITACAO >= SD.DT_REF AND CE.DT_CREDITO <= SD.DT_REF) OR (CE.CD_SITUACAO = 5 AND CE.DT_QUITACAO < SD.DT_REF AND (SELECT COUNT(*) FROM DBINTECH.DBO.CE_PRESTACOES CP WHERE CP.CD_FUNDACAO = CE.CD_FUNDACAO AND CP.ANO_CONTRATO = CE.ANO_CONTRATO AND CP.NUM_CONTRATO = CE.NUM_CONTRATO AND CP.SEQ_PREST < 1000 AND (CP.DT_PAGTO IS NULL OR (CP.DT_PAGTO > SD.DT_REF AND CP.DT_VENC < SD.DT_REF)) AND CP.TIPO IN ('P', 'I') AND CP.CD_ORIGEM_REC < 51) > 0) ) AND SD.CD_EMPRESA = '0002' AND SD.CD_PLANO = 1 --PLANO BD GROUP BY SD.DT_REF, SD.CD_EMPRESA, SD.CD_PLANO ORDER BY SD.DT_REF, SD.CD_EMPRESA, SD.CD_PLANO SELECT @VL_CONCESSAO_CE = SUM(MD.VL_CONCESSAO) FROM DBIntech.dbo.CE_MOVIMEN_DIARIA_CONTRATO MD, DBIntech.dbo.CE_CONTRATOS CE WHERE MD.CD_FUNDACAO = '01' AND MD.DT_REF = @DT_REF_COTA_CE AND MD.CD_EMPRESA = '0002' AND MD.CD_PLANO = 1 --PLANO BD AND CE.CD_FUNDACAO = MD.CD_FUNDACAO AND CE.ANO_CONTRATO = MD.ANO_CONTRATO AND CE.NUM_CONTRATO = MD.NUM_CONTRATO GROUP BY MD.DT_REF, MD.CD_EMPRESA, MD.CD_PLANO ORDER BY MD.DT_REF, MD.CD_EMPRESA, MD.CD_PLANO END --Fim do IF @TIPO_GERAL = 'CE' --Pápeis de IMOVÉIS IF @TIPO_GERAL = 'IM' BEGIN SELECT @VL_OPER_COTA_IM = COTA.VL_REC_DESP_COTA, @PATRIMONIO_IM = COTA.VL_PL_COTA FROM DBINTECH.DBO.IM_RENTAB_COTA COTA INNER JOIN DIM_PLANO DP ON DP.CART = COTA.CART WHERE CONVERT(DATE,COTA.DT_REF_COTA) = @COMPARA_DATA AND DP.SK_DIM_PLANO = 3--PLANO BD AND COTA.ADM = 9999999 ORDER BY 1 END --Fim do IF @TIPO_GERAL = 'IM' --Pápeis de RENDA FIXA IF @TIPO_GERAL = 'RF' --Papaeis do RF BEGIN SELECT @VL_OPER_COTA_RF = COTA.VL_OPER_COTA, @PATRIMONIO_RF = COTA.VL_PL_COTA FROM DBINTECH.DBO.RF_RENTAB_COTA COTA INNER JOIN DIM_PLANO DP ON DP.CART = COTA.CART WHERE CONVERT(DATE,DT_REF_COTA) = @COMPARA_DATA AND PAPEL_RF ='ZZZZZ' AND DP.SK_DIM_PLANO = 3--PLANO BD AND ADM = 9999999 AND ANO_PAP = 0 AND SEQ_PAP = 0 ORDER BY DT_REF_COTA END --Fim do IF @TIPO_GERAL = 'RF' --Pápeis de RENDA VARIÁVEL IF @TIPO_GERAL = 'RV' BEGIN SELECT @VL_OPER_COTA_RV = COTA.VL_OPER_COTA, @VL_DIVID_PREV_RV = COTA.VL_DIVID_PREV, @PATRIMONIO_RV = COTA.VL_MERC_COTA FROM DBIntech.dbo.RV_RENTAB_COTA COTA INNER JOIN DIM_PLANO DP ON DP.CART = COTA.CART_COTA WHERE CONVERT(DATE,COTA.DT_REF_COTA) = @COMPARA_DATA AND DP.SK_DIM_PLANO = 3--PLANO BD AND COTA.TIPO_COTA = 'ZZZZZ' AND COTA.PAPEL_COTA = 'ZZZZZZZZZZ' AND COTA.ADM_COTA = 999999999 ORDER BY 1 END --Fim do IF @TIPO_GERAL = 'RV' SELECT @VL_OPER_COTA_TOTAL = @VL_OPER_COTA_TOTAL + @VL_OPER_COTA_RV + @VL_OPER_COTA_RF + @VL_OPER_COTA_IM + @VL_CONCESSAO_CE SELECT @VL_DIVID_PREV_TOTAL = @VL_DIVID_PREV_TOTAL + @VL_DIVID_PREV_RV SELECT @PATRIMONIO_TOTAL = @PATRIMONIO_TOTAL + @PATRIMONIO_RV + @PATRIMONIO_IM + @PATRIMONIO_RF + @VL_SALDO_ATUAL_CE SELECT @VL_AMORTIZACAO_CE = 0, @VL_SALDO_ATUAL_CE = 0, @VL_CONCESSAO_CE = 0, @VL_OPER_COTA_IM = 0, @PATRIMONIO_IM = 0, @VL_OPER_COTA_RF = 0, @PATRIMONIO_RF = 0, @VL_OPER_COTA_RV = 0, @VL_DIVID_PREV_RV = 0, @PATRIMONIO_RV = 0 SET @QNT_LINHAS = @QNT_LINHAS - 1 END --Fim do loop da ##TEMP_AUX_BD --Primeiro calculo da rentabilidade IF @COMPARA_DATA = @DATA_REFERENCIA BEGIN SELECT @VL_COTA_TOTAL = 100 SELECT @QTD_COTA_TOTAL = @PATRIMONIO_TOTAL / @VL_COTA_TOTAL SELECT @AUX_QNT_COTA_ANTERIOR_TOTAL = @QTD_COTA_TOTAL END --Cálculos seguintes da rentabilidade ELSE BEGIN SELECT @VL_COTA_TOTAL = ((@PATRIMONIO_TOTAL - @VL_OPER_COTA_TOTAL) + @VL_DIVID_PREV_TOTAL) / @AUX_QNT_COTA_ANTERIOR_TOTAL SELECT @QTD_COTA_TOTAL = @PATRIMONIO_TOTAL / @VL_COTA_TOTAL SELECT @AUX_QNT_COTA_ANTERIOR_TOTAL = @QTD_COTA_TOTAL END --Zera as variáveis que realizam o cálculo SET @VL_OPER_COTA_TOTAL = 0 SET @VL_DIVID_PREV_TOTAL = 0 SET @PATRIMONIO_TOTAL = 0 SELECT @COMPARA_DATA = DBO.DATA_SEGUINTE(@COMPARA_DATA) END --Fim do loop do @COMPARA_DATA <= @DATA SET @RENTABILIDADE_MES = ((@VL_COTA_TOTAL/100) - 1) * 100 INSERT INTO FATO_RENTABILIDADE_GRUPO VALUES(3,@SK_DT_REF_SALDO,@RENTABILIDADE_MES,NULL) END
-
Olá Rafael,
Seria muito mais performático se você tentasse eliminar os laços e trabalhar de forma "Set Based".
Abraço
MCP – MCTS – MCITP – MCT SQL Server ADM / BI / Dev Sharepoint IBM Optim Certification http://demetriosilva.wordpress.com/
-