Na consulta a seguir, preciso contar as transações de cada cliente. No entanto, tenho que excluir totalmente do conjunto de resultados os clientes que possuem uma transação com mais de um ano.
O otimizador de consulta não deveria ser inteligente o suficiente para avaliar a existência apenas uma vez para cada cliente?
--Count transactions on customers that are less than 1 year old
SELECT t1.CUSTID, COUNT(*)
FROM CUST_TRX t1
WHERE NOT EXISTS (
SELECT FIRST 1 1
FROM CUST_TRX t2
WHERE
t2.CUSTID = t1.CUSTID AND
t2.DATED < CURRENT_DATE - 365
GROUP BY t2.CUSTID
)
GROUP BY t1.CUSTID
Não há recursos naturais em meu plano de consulta. Essa consulta está sendo executada como se o banco de dados estivesse executando a cláusula de existência para cada transação, em vez de executá-la para cada cliente. O desempenho é o mesmo se eu remover o GROUP BY
na subconsulta.
Existe uma maneira melhor de fazer isso para que eu possa obter um melhor desempenho do banco de dados? Esperançosamente, uma consulta simples SELECT
funcionará evitando um CTE, se possível (isso apresentaria outros desafios).
Devido a outros GROUP BY
critérios (não mostrados aqui) não estou conseguindo simplesmente verificar MIN(DATED)
, preciso muito realizar outra consulta.
Com consultas como esta, muitas vezes é mais eficiente executar uma verificação
LEFT OUTER JOIN
em vez daNOT EXISTS
verificação de estilo, geralmente implica uma varredura de índice completa (ou varredura de tabela sem os índices corretos no lugar), mas com muitas linhas na(s) tabela(s) principal(is) isso é menos caro do que o grande número de buscas de índice (uma na tabela de referência para cada linha retornada da tabela principal) que, de outra forma, resultaria em . Alguns planejadores de consulta são bastante inteligentes em identificar essa equivalência e usar o plano alternativo onde é a melhor escolha, mas não parece que isso tenha acontecido no seu caso.Tente algo como:
(nota: não estou familiarizado com o firebird, então a sintaxe acima pode precisar de ajustes, mas deve ilustrar o ponto)
Sem
WHERE t2.CUSTID IS NULL
todas as linhas det1
com correspondências emt2
será exibida uma vez para cada correspondência encontrada emt2
e aquelas sem correspondênciast2
serão exibidas uma vez, mas com quaisquer colunas selecionadas desse objeto definido como NULL. AWHERE
cláusula então filtra as correspondências.Dependendo das habilidades do mecanismo de banco de dados, especialmente se a quantidade de dados no objeto de referência (
CUST_TRX
com um filtro aplicado aqui) for grande, isso pode ser significativamente menos eficiente do que as opçõesWHERE <something> NOT IN
ouWHERE NOT EXISTS
, portanto, compare primeiro conjuntos de dados realistas antes de usar o método. Muitas vezes, funciona muito mais eficientemente com o MS SQL Server nos casos em que o planejador de consultas não percebe que oWHERE NOT IN
arranjo pode ser executado dessa maneira com mais eficiência.Além disso, se você fizer isso dessa maneira, deixe um comentário no código (e/ou na documentação de suporte) para dizer que está fazendo isso como um equivalente
WHERE <something> NOT IN
ouWHERE NOT EXISTS
que espera ser mais eficiente. Você se lembrará disso e uma pessoa experiente em SQL reconhecerá o padrão, mas outras pessoas que olham para o código podem não entender imediatamente a intenção/razão e voltar a usá-loWHERE NOT EXISTS
para maior clareza, pois é melhor lido do que na frase em inglês.Quando você diz "contar transações em clientes com menos de 1 ano", você quer dizer:
Pelo código de exemplo, entendo que o número 1 é o que você deseja. Nesse caso, você realmente precisa de um WHERE NOT EXISTS? Você poderia apenas fazer algo como:
Não sou usuário do Firebird, mas pesquisei a sintaxe GROUP BY / HAVING.
[EDIT]Excluir do conjunto de resultados os clientes que possuem uma transação com mais de um ano.
OK, aqui está outra abordagem para agregar as linhas para eliminar o cliente da seleção.
[EDIT] OK, então a consulta é mais complexa que pode ser incorporada em uma única consulta.
Isso significa que você provavelmente precisará usar um padrão muito parecido com o que você postou primeiro. Observe que EXISTS implica um DISTINCT e geralmente é mais rápido que um JOIN de um SELECT DISTINCT. Mas você pode tentar abordagens diferentes e comparar o comportamento, tempo, etc. Em seguida, escolha a que você mais gosta.