[firebase-br] Consulta demorando 2 horas como resolvo.

Eduardo Bahiense eduardo em icontroller.com.br
Sáb Jun 4 20:19:26 -03 2011


Olá Luciano

Desculpe se chego atrasado nesta thread, mas uso o FB em ambientes de 
alta requisição e, por ter certeza de sua performance e segurança, 
sinto-me na obrigação de analisar esse caso.
Primeiramente, permita-me organizar essa consulta, pois, código SQL deve 
ser tratado como qualquer linguagem de programação: indenta-se para dar 
melhor entendimento, especialmente se a submetemos a terceiros.

select
     cm.codigo,
     cm.codigo_barras,
     cm.codigo_fabricante,
     cm.cod_fornecedor,
     cm.mercadoria,
     cf.fornecedor,
     Max(Case When(Gp.filial = 1) Then Gp.preco_g_vista End) As Preco_AS,
     Max(Case When(Gp.filial = 2) Then Gp.preco_g_vista End) As Preco_MT
from
     cadastro_mercadorias cm
     inner join cadastro_fornecedor cf on (cf.codigo = cm.cod_fornecedor)
     Inner Join grade_produtos GP on (Gp.cod_produto = CM.codigo)
Where
     exists (select
                 cc.codigo_barras
             from
                 cadastro_mercadorias cc
             where
                 cc.codigo_barras = cm.codigo_barras
             group by
                 1
             having count(cc.codigo_barras) > 1
             )
group by
     1, 2, 3, 4, 5, 6
order by
     cm.codigo_barras

Bem, a primeira coisa que chama a atenção nessa consulta é o "order by", 
no 2º campo. Como o Eduardo J. já mencionou, o próprio "group by" já 
ordena, assim, um
"group by 2,1,3,4,5,6", ou um
     select
         cm.codigo_barras,
         cm.codigo,
         ...
     group by
         1, 2, 3, 4, 5, 6

evitaria o "order by", resultando em melhor performance, especialmente 
se o resultado dessa consulta envolver milhares de linhas.

Fora essa questão, a cláusula "select" está ok, nada a comentar.
Quanto aos "joins", é preciso saber se há índices em:
     cadastro_fornecedor.codigo
     grade_produtos.cod_produto
para que essas junções não tenha problema de performance.
Outra coisa a se considerar é que "inner join" quando o lado direito 
possui pouca correlação, pode ser muito penoso.
Se você está usando "inner join" é porque está esperando que haja 
produtos sem código de fornecedor ou sem código de grade_produto, assim, 
você poderia, testar a performance com "left join" com uma clasa "where" 
do tipo:
     where
         cm.cod_fornecedor is not null
         and CM.codigo is not null
         and exists(...)

Verificadas essas questões, passamos para a cláusula "where" em si:

Um "where exists(...)", por si só, obriga a busca natural, ou seja sem 
índices, assim, todos os registros de "cadastro_mercadorias" serão, 
obrigatoriamente lidos, e uma outra segunda consulta será executada 
(select do exists) para cada linha de "cadastro_mercadorias".
Bem, considerando o fato de que cada linha em "cadastro_mercadorias" vai 
gerar um segunda consulta, esta deve ser cuidadosa quanto à performance, 
então, vamos dar uma olhada:

     select
         cc.codigo_barras
     from
         cadastro_mercadorias cc
     where
         cc.codigo_barras = cm.codigo_barras
     group by
         1
     having count(cc.codigo_barras) > 1

Primeiramente, temos que analisar se há um índice em 
"cadastro_mercadorias.codigo_barras", porque se não houver índice, para 
cada linha percorrida em "cadastro_mercadorias", haverá uma varredura em 
todas as linhas dessa mesma tabela para pesquisar esse "where".
Verificado o índice, observe que exists(...) não depende de um campo, 
basta "select 1", ao invés de "select cc.codigo_barrsas". Não que isso 
seja determinante na performance, mas se estamos falando em otimizar...
O group by neste subselect também é totalmente dispensável, se o 
construirmos dessa forma:

where (
     select
         count(1)
     from
         cadastro_mercadorias cc
     where
         cc.codigo_barras = cm.codigo_barras
) > 1

Se houver índices adequados, mesmo não sendo a melhor construção, você 
não deveria ter problemas de performance, e isso nos remete a uma 
afirmativa sua:

"Ele vai retornar produtos com o codigo de barras duplicados. eu sei que 
deve ter SQLs mais simples para esse proposito só que agora achei 
interessante o porque o PostGreSQL ser tão mais veloz".

Acho improvável que o PG consiga performance muito melhor nas mesmas 
condições de volume de dados e planejamento de índices. O mais provável 
é que você tenha movido dados do PG para o FB sem trazer o planejamento 
de índices, contudo, se o que você quer e identificar produtos com 
código de barras duplicados, dentro da estrutura inicial que você informou

select
     cm.codigo_barras,
     cm.codigo,
     cm.codigo_fabricante,
     cm.cod_fornecedor,
     cm.mercadoria,
     cf.fornecedor,
     Max(Case When(Gp.filial = 1) Then Gp.preco_g_vista End) As Preco_AS,
     Max(Case When(Gp.filial = 2) Then Gp.preco_g_vista End) As Preco_MT
from
     cadastro_mercadorias cm
     inner join cadastro_fornecedor cf on (cf.codigo = cm.cod_fornecedor)
     Inner Join grade_produtos GP on (Gp.cod_produto = CM.codigo)
group by 1,2,3,4,5,6
having count(*) > 1

Observe apenas que você está pedindo se há codigos de barras duplicado 
para cada conjunto fornecedor/fabricante/mercadoria.

Dá para conversar muito sobre tudo isso, mas já falei demais para um post.

Sucesso,

Eduardo









Mais detalhes sobre a lista de discussão lista