Bom dia pessoal, estava querendo melhorar a performance de um SELECT que
trata os dias uteis vencidos de determinada duplicata, para isso tentei
alguns métodos, vejamos:

Obs: para o exemplo que citarei abaixo, não levarei em conta os feriados.

SELECT
CODIGO,
CODCLIENTE,
VENCIMENTO,
CASE WHEN CURRENT_DATE > VENCIMENTO THEN
   CURRENT_DATE - VENCIMENTO - (SELECT COUNT (VENCIMENTO + GENERATE_SERIES)
FROM GENERATE_SERIES(0, CURRENT_DATE - VENCIMENTO) WHERE EXTRACT(DOW FROM
   VENCIMENTO + GENERATE_SERIES) = 0)
   ELSE 0
END AS diasuteisvencidos


FROM
CONTASARECEBER
WHERE
SITUACAO = 0

Esta consulta me retorna 15827 rows em 4415 ms.
Veja que para torná-la mais rápida preferi, ao invés de contar os dias
úteis, subtrair os dias não úteis (domingo, neste caso).

O engraçado é que se eu montar uma função para isto a consulta fica quase
40% mais lenta, vejamos:

CREATE OR REPLACE FUNCTION DIASNAOUTEIS(DATAINICIAL DATE, DATAFINAL DATE,
SABADO_EH_DIA_UTIL BOOLEAN DEFAULT TRUE) RETURNS BIGINT AS
$$
SELECT
COUNT(DIA)
FROM
GENERATE_SERIES(0, CAST($2 AS DATE) - CAST($1 AS DATE)) AS DIA
WHERE
EXTRACT(DOW FROM DIA + CAST($1 AS DATE)) IN (0, (CASE WHEN $3 THEN 0 ELSE 6
END))
$$
LANGUAGE SQL;


SELECT
CODIGO,
CODCLIENTE,
VENCIMENTO,
 CASE WHEN CURRENT_DATE > VENCIMENTO THEN
CURRENT_DATE - VENCIMENTO - DIASNAOUTEIS(VENCIMENTO, CURRENT_DATE)
        ELSE 0 END AS diasuteisvencidos
FROM
CONTASARECEBER
WHERE
SITUACAO = 0


A consulta acima me retorna os mesmos 15827 registros em 6117 ms


Utilizando WITH o desempenho melhora bastante, vejamos:

CREATE OR REPLACE FUNCTION DIASNAOUTEIS(DATAINICIAL DATE, DATAFINAL DATE,
SABADO_EH_DIA_UTIL BOOLEAN DEFAULT TRUE) RETURNS SETOF DATE AS
$$
SELECT
DIA + CAST($1 AS DATE)
FROM
GENERATE_SERIES(0, CAST($2 AS DATE) - CAST($1 AS DATE)) AS DIA
WHERE
EXTRACT(DOW FROM DIA + CAST($1 AS DATE)) IN (0, (CASE WHEN $3 THEN 0 ELSE 6
END))
$$
LANGUAGE SQL;



WITH QUERY AS (SELECT DIASNAOUTEIS( (SELECT MIN(VENCIMENTO) FROM
CONTASARECEBER WHERE SITUACAO = 'ABERTA'), CURRENT_DATE) )
SELECT
CODIGO,
CODCLIENTE,
VENCIMENTO,
CASE WHEN CURRENT_DATE > VENCIMENTO THEN
CURRENT_DATE - VENCIMENTO - (SELECT COUNT(DIASNAOUTEIS) FROM QUERY WHERE
DIASNAOUTEIS BETWEEN VENCIMENTO AND CURRENT_DATE)
ELSE 0 END AS DIASUTEISVENCIDOS
FROM CONTASARECEBER WHERE SITUACAO = 0


A consulta acima me retorna os mesmos 15827 registros em 1871 ms

Alguém tem alguma dica/sugestão pra otimizar ainda mais?


-- 

Zeus Automação Comercial*
*ADENILTON Batista da Silva
Fones:(79)3431-6392/3431-8381
_______________________________________________
pgbr-geral mailing list
[email protected]
https://listas.postgresql.org.br/cgi-bin/mailman/listinfo/pgbr-geral

Responder a