Olá pessoal!
Este post inicia uma série de posts com dicas de ajustes de SQL, direcionado principalmente para desenvolvedores. Aos interessados, recomendo também a leitura da série Tuning durante o projeto: boas práticas. Iniciarei com as dicas mais simples – e que podem parecer muito óbvias para todos os DBA’s e também para alguns desenvolvedores mais experientes – mas que muitas vezes não são praticadas por desenvolvedores, seja por falta de conhecimento ou por falta de noção da importância de pequenos detalhes ao se escrever códigos SQL. Esses detalhes podem significar uma significativa diferença no desempenho de uma aplicação – seja pra melhor, seja pra pior.
USO DE JOINS
Vamos começar com 2 casos muito simples, ambos envolvendo o uso de JOINS entre tabelas.
O que pode parecer muito óbvio para um DBA, as vezes não é praticado por um desenvolvedor. Vamos usar o já conhecido schema HR como exemplo. Imaginem que um desenvolvedor quer uma informação muito simples: O nome de um um funcionário, seu código, e o departamento onde trabalha.
Ele conseguiu os dados acima, todos na mesma tabela, de uma forma bem simples e direta. Porém, seu relatório exigia o nome do Departamento (e não apenas seu código), e por isso ele executa OUTRO select:
Dentro da aplicação, ele guarda os resultados em variáveis de memória, para depois mostrar os resultados, seja na tela do usuário final ou na impressão de um relatório. Ele obteve seu resultado após duas consultas, ao invés de apenas uma consulta utilizando JOIN. Aos olhos do usuário final, tudo OK: ele obteve corretamente a informação que desejava aos seus olhos. Mas e o desempenho da aplicação? Será que esta é a melhor maneira de utilizar o banco de dados?
Não, esta não é a melhor maneira. Se os seus dados podem ser obtidos através de apenas um SELECT – ao invés de dois (ou mais), então use apenas um SELECT. Pode haver exceções em alguns casos, mas via de regra uma consulta é sempre menos custosa para o banco de dados do que duas consultas.
Assim ficaria a consulta feita maneira correta:
Este é um exemplo muito simples e também muito óbvio, porém podemos lembrar que isto não fica assim tão óbvio quando falamos de querys muito mais complexas. As vezes o desenvolvedor pode acabar não conseguindo todas suas informações em apenas um Select. Nestes casos ele pode – e deve – pedir ajuda ao DBA para chegar na melhor solução.
Cláusula WHERE
A segunda dica deste post é em relação a ordenação dos JOINS. Vamos supor que seu SELECT faça JOINs entre 4 tabelas, chamadas T1, T2, T3 e T4. Vamos supor também que as chaves estrangeiras para realizar os JOINs estejam exatamente nesta mesma ordem, ou seja, T1 com T2, T2 com T3, e T3 com T4. Ou seja, o SELECT poderia ficar assim:
Para obter o melhor desempenho de um SELECT neste caso, uma medida bem simples pode ser tomada: a prioridade dos JOINs deve ser da maior tabela para a menor tabela (considerando o número de registros). Vamos considerar a seguinte quantidade de registro para cada tabela:
T1 = 20
T2 = 80.000
T3 = 500
T4 = 150
Neste caso, o SELECT ficaria com uma melhor performance com o seguinte código:
O resultado, ou seja, os dados retornados são exatamente os mesmos. Repare que a única mudança foi a ordem com que foram feitos os JOINs. Primeiro devem ser feitos os mais “destrutivos”, ou seja, o JOIN que elimina mais linhas do result set. O objetivo é fazer com o que o Oracle trabalhe com o menor número possível de registros em memória: quanto mais registros são eliminados na primeira condição da cláusula WHERE, mais próximos estarão os dados do resultado final.
Isto não vale apenas para JOIN’s, mas também para outros tipos de condições na cláusula WHERE. Se no SELECT acima fosse possível usar um unique index na tabela T2, que tem 80 mil linhas, eliminaríamos de cara as outras 79.999 linhas do área de memória ocupada pelo Oracle para realizar a operação.
Esta é uma dica para desenvolvedores que vale para qualquer banco de dados: fazer com que o gerenciador trabalhe com o menor número possível de registros “o quanto antes”. A cláusula WHERE deve estar, portanto, ordenada da condição “mais destrutiva” (primeiro) para a “menos destrutiva” (por último).
Muito bom Milton, parabéns pelo post. Muito bacana mesmo.
Abraço,
Rodrigo Santana.
Obrigado Rodrigo!
Abraço
Olá Milton!
A dica de fazer os JOIN das tabelas mais destrutivas para as menos destrutivas vale também para quem trabalha com INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN ? Ou isso só faz diferença pra quem trabalha fazendo as condições de ligação no WHERE ?
O plano gerado entre essas duas query são diferentes ? Ou o plano vai ser o mesmo, mas o desepenho da segunda query é melhor ?
SELECT T1.campo1, T4.campo2
FROM T1, T2, T3, T4
WHERE T1.fk = T2.id
AND T2.fk = T3.id
AND T3.fk = T4.id;
SELECT T1.campo1, T4.campo2
FROM T1, T2, T3, T4
WHERE T2.fk = T3.id
AND T3.fk = T4.id
AND T1.fk = T2.id;
Sakamoto
MyTracelog – Registro de um DBA
http://mytracelog.blogspot.com
Olá Sakamoto!
Vi que você é de Maringá, correto?
Eu moro em Curitiba agora, mas morei 7 anos em Maringá – fiz minha graduação na UEM inclusive. Trabalha onde aí? Benner? DB1?
Bom, vou tentar responder tuas perguntas. Ainda não sou nenhum expert em Tuning – estou aprendendo e ainda tenho muito o que aprender. Tanto é que estou com este blog justamente para me ajudar a fixar o que vou aprendendo.
Eu particularmente NUNCA uso “INNER JOIN”, “LEFT OUTER JOIN”, etc. Escrevo minhas querys com comparações diretas. Por isso não posso te afirmar com 100% de certeza, porém acredito que a questão de performance vale sim, da mesma forma! No meu entender, muda apenas a sintaxe, a maneira de escrever – internamente acredito que o plano de execução seja o mesmo.
Bom, sobre a comparação do plano de execução entre as duas querys, darei a resposta na prática: vou simular um caso igual a esse numa base de testes, gerar os planos de execução e então colo aqui no meu blog, combinado?
Abraço
Sim, sou de Maringá, me formei em Ciência da Computação na UEM em 2007.
Já trabalhei em diversas empresas aqui em Maringá, o ultimo emprego eu era programador senior na Elotech, conhece ?
Atualmente sou DBA da Prefeitura Municipal de Maringá, acabei de assumir o cargo, estou começando minha carreira de DBA agora…
Teve uma epoca que você estava desempregado, já esta trabalhando ?
Excelente Post sócio, parabéns novamente pelo Blog
Grande Sócio!
Valeu, muito obrigado!
Abraço!
Olha eu aqui!Esse post é bem interessante, em meus sqls uso muito o inner, left, outer, right joins pelo menos eu acho mais limpo, também não sou expert em tunning, mas acredito que vai depender muito do cenário o qual se trabalha.
hardware, memória,testes, etc.
Abraços!
Emerson
DBA Jr
emersonmartinsdba.blogspot.com
Bom Dia Muito Bom seu Post. Tenho Uma Duvida ao declarar mais de uma tabela no from o compilador do oracle tem alguma ordem ou tem como saber se muda o tempo de execução a ordem das tabelas no from referente a quantidade de dados que tem na tabela?
Olá Luiz Gustavo!
Cara, na verdade há erros neste post que eu criei.
Usei um livro como referência, mas infelizmente parece que o livro estava errado.
Nas versões bem antigas do Oracle havia sim diferença, tanto no From quanto no Where. Hoje não existe mais isso. Graças às coletas de estatísticas o otimizador do Oracle escolhe o melhor caminho para resolver a tarefa, ok?
Portanto desconsidere os trechos que falo o contrário neste post – ainda não tive tempo de fazer esta correção.
Abraço!
Milton, se eu não me engano, vc não esta 100% errado. Hoje trabalho com Oracle 11g e quando as tabelas são muito grandes, a ordem no FORM e/ou WHERE melhora o Cost. Em cenários pequenos não consigo ver diferença, porém nos grandes sim. Ahh.. eu também era de Maringá. Rsrsrsrs