sexta-feira, outubro 30, 2009

Dicas para seu SQL não feder

No shit
Pra mim, a melhor tradução de code smells é fedentina de código.

A idéia de fedentina em código foi popularizada pelo ótimo livro Refactoring, do Martin Fowler. É uma indicação de que algo no código não está bem e merece ser analisado mais de perto. É como você andando na rua, sente um cheiro desagradável e olha a sola de seu sapato para checar se está tudo certo.

Tive que rever milhares de linhas de código SQL. Depois de um tempo, você deixa de tentar entender cada detalhe de comando e começa a funcionar por reconhecimento de padrões. Basta passar os olhos pelo código e logo vem aquele cheiro que tem algo mal feito por ali. Ao contrário do exemplo da sola de sapato, na maior parte das vezes há uma grande imundície.

Compartilho então com você, minha coleção de fedentinas em SQL:
DISTINCT
Uma vez, estava eu no departamento de TI de uma grande empresa e ouvi comentarem: "Já fiz o select, o único problema é que está vindo com alguns dados duplicados.", logo alguém respondeu: "É fácil resolver, basta usar o DISTINCT.". Argh! São raro os casos em que um DISTINCT é necessário. E normalmente o são por alguma falta de normalização no modelo de dados.

Ao usar o DISTINCT sem entender a query, você pode estar escondendo um lentíssimo produto cartesiano, ou mesmo um erro no seu código. Já vi códigos como este: SELECT DISTINCT P.Nome FROM pessoas P, equipe e WHERE e.id=123.

Parece estar tudo muito bom na sua base de testes quando você tem apenas uma única equipe e uma dúzia de pessoas. Quando entrar em produção: CABOOOM!!! Tudo vai estourar.

O mais provável é que o DISTINCT esteja acobertando a inexistência de um join na sua query. É preciso analisar a query com cuidado e checar não precisa acrescentar um join a mais entre as tabelas. Talvez seja o caso até de remodelar o projeto do banco de dados.

LIMIT 1, TOP 1, ou RowNum <=1
Esta é outra forma de esconder uma falta de join. O LIMIT é do MySql, o TOP 1 do SqlServer, e o RowNum do Oracle.

De vez em quando, sua consulta vai retornar mais de um elemento e você não entende o porquê. Qual a solução mais fácil? Ora, pegar só o primeiro e continuar correndo para entregar as coisas no prazo. Só terá é um bug sinistro pra pegar em produção. Afinal de contas, parar, pensar e olhar qual join poderia fazer a mais, ou que tabela está faltando incluir é muito difícil.

E tem o caso em que isto está 100% errado: é quando você usa um TOP 1 e não usa um ORDER BY. Como o SQL só trabalha com conjuntos, pegar o primeiro elemento sem ordenar antes, quer dizer que você está pegando um elemento aleatório. Pode até ser que seu SGBD seja sempre retornado o que você deseja, mas pequenas modificações no banco, como a criação de um novo índice, podem fazer com que um valor errado seja retornado de uma hora pra outra.

GROUP BY sem funções de agregação
Este é outro truque para evitar resultados repetidos e garantir um código desnecessariamente lento. Se nos resultados da query não tem nenhuma função COUNT, MAX, MIN ou similares, tem um erro aí. Certamente é mais um caso de join faltando que precisa ser investigado.

O caso mais inócuo é quando é usado no lugar de um DISTINCT. Tem gente que acha que o GROUP BY seria mais eficiente. Isto não é verdade. Qualquer que seja seu SGBD, ele será esperto o bastante para fazer o mesmo plano para as duas consultas. O GROUP BY apenas deixará seu código mais difícil de entender e manter, pois todas colunas do resultado precisarão ser repetidas no final.

Subselects
Neste caso, uma das colunas de retorno da query é uma outra query, isto é, um subselect. Ou pior, várias colunas são subselects parecidos, talvez retornando apenas um COUNT. Isto pode ser a coisa mais lerda do mundo.

Tem que pensar que cada subselect será executado para cada registro retornado. Se você retorna 100 registros, estará fazendo 101 consultas! A solução costuma ser mais complicada, normalmente usando condicionais e GROUP BY. Só que vale o preço. O tempo da consulta pode diminuir ordens de grandeza.

O fato de SQL ser uma linguagem declarativa às vezes disfarça processamentos custosos. Somente incluir um comando ali não parece que sua consulta está ficando duas ordens de grandeza mais lenta. Pode até ser que seu BD seja esperto o bastante para otimizar estas queries. Acredito até que isto já tenha salvo muitos sistema sem produção. Mas pra que confiar?

Outer joins
Esta fedentina aqui acontece mais em código procedural. O sujeito faz um loop nos resultados de um select. Para cada elemento retornado, processa alguma coisa e faz um outro select para checar se existe algo em outra tabela. Se existir faz um processamento a mais. Se você tiver 10 elementos, serão 11 selects. 11 idas à base para pegar dados.

Um Outer join resolveria isto com uma única consulta! Caso não existisse relação entre as tabelas, os elementos da tabela secundária viriam como NULL. Somente isto pode fazer a diferença entre um banco de dados stressado e um tranquilo.

Por outro lado, Outer joins são mais caros que os inner joins tradicionais. Já vi gente fazendo um left join "só pra garantir". Confira sempre se é mesmo necessário fazê-lo em vez de um inner join normal.

texto LIKE '%:nome%'
Se a string com o que o LIKE compara começa com %, significa que o texto pode casar em qualquer ponto. O SBGD ê terá não só que varrer não só toda sua tabela, como todo o texto do atributo analisado. É um algoritmo de complexidade quadrática! Sim, isto é lento praca.

Este é mais um caso em que na base de testes, que tem pouco dados, tudo ficará uma beleza. Alguns meses (ou anos) depois que o sistema entrar em produção, tudo emperrará.

A solução para este problema é usar um índice textual. Mas aí é assunto para outro post.

Ver estas e outras em códigos de produção me leva a divagar sobre dois grandes sucessos da informática, o MySql e os frameworks de mapeamento Objeto-Relacional.

Até outro dia o MySql não tinha subselects. Isto era chato, pois tornava necessário codificar coisas que uma única consulta mais sofisticada resolve. Só que por outro lado, ficava claro pra todo mundo que você estava fazendo loop dentro de loop. Nenhum problema ficava "escondido" no código. A falta de features mais avançadas, deixava claro tudo que estava sendo feito.

Os frameworks Objeto-Relacional por outro lado fazem tudo pra você. Eles não esquecem dos joins, nem inventam gambiarras pra retornar o que desejam. Alguns até criam todos os índices pra você, um outro problema que nem mencionei. No mundo da produção de código industrial, isto é um aumento de produtividade tremendo. Vai ser chato quando quiser fazer um relatório mais complicado, mas aí basta ter alguém que entenda como o mapeamento é feito e faça a consulta "na unha", com SQL de verdade.

As limitações destas tecnologias, acabam sendo uma grande vantagem. Elas resolvem a maior parte dos problemas. Sem elas, fica muito fácil dar um tiro pé. Putz! O tiro ainda foi no pé que tava todo sujo!

quarta-feira, outubro 28, 2009

Código de ética do engenheiro de software

  1. Público: Engenheiros de Software devem agir de forma consistente com o interesse público.
  2. Cliente e empregador: Engenheiros de Software devem agir de modo que atenda aos melhores interesses de seus clientes e empregadores, sempre consistente com o interesse público
  3. Produto: Engenheiros de Software devem garantir que seus produtos atendem ao mais alto padrão de qualidade profissional.
  4. Julgamento: Engenheiros de Software devem manter integridade e independência em seus julgamentos profissionais.
  5. Gerência: Engenheiros de Software que sejam gerentes e líderes devem subscrever e promover uma abordagem ética à gerência de desenvolvimento e manutenção de software.
  6. Profissão: Engenheiros de Software devem avançar a integridade e reputação da profissão, sempre de forma consistente com o interesse público.
  7. Colegas: Engenheiros de Software devem ser justos com seus colegas e sempre apoiá-los.
  8. Sobre si próprio:Engenheiros de Software devem, durante toda sua vida, estar sempre aprendendo as práticas de sua profissão, e promoverem uma abordagem ética à profissão.
Sempre é bom de ler e saber que existe.

Em português o texto, que já era mal escrito em inglês, ficou ainda pior. Leia o documento original: Código de Ética do Engenheiro de Software, onde cada item é detalhado.