[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