Bene, entrambe le tue query si trovano su tabelle diverse (reportimpression
rispetto a reportimpressionday
), quindi il confronto delle due query in realtà non è un confronto. Hai ANALYZE
Entrambi? Anche varie statistiche di colonna possono svolgere un ruolo. L'indice o il rigonfiamento della tabella potrebbero essere diversi. La maggior parte di tutte le righe si qualifica per febbraio 2019? ecc.
Un colpo al buio, confronta le percentuali di entrambi i tavoli:
SELECT tbl, round(share * 100 / total, 2) As percentage
FROM (
SELECT text 'reportimpression' AS tbl
, count(*)::numeric AS total
, count(*) FILTER (WHERE datelocal >= '2019-02-01' AND datelocal < '2019-03-01')::numeric AS share
FROM reportimpression
UNION ALL
SELECT 'reportimpressionday'
, count(*)
, count(*) FILTER (WHERE datelocal >= '2019-02-01' AND datelocal < '2019-03-01')
FROM reportimpressionday
) sub;
È quello per reportimpression
più grande? Quindi potrebbe semplicemente superare il numero per il quale ci si aspetta che un indice possa aiutare.
In genere, il tuo indice reportimpression_datelocal_index
on (datelocal) sembra buono e reportimpression_viewership_index
consente anche scansioni solo indice se l'autovacuum supera il carico di scrittura sul tavolo. (Anche se impressions
&agegroup
sono solo merci morte per questo e funzionerebbe ancora meglio senza).
Risposta
Hai il 26.6 percent, and day is 26.4 percent
per la mia domanda. Per una percentuale così elevata, gli indici in genere non sono per niente utili . Una scansione sequenziale è in genere il modo più veloce. Solo le scansioni solo indice possono ha ancora senso se la tabella sottostante è molto più grande. (Oppure hai grave table bloat e indici meno gonfi, il che rende gli indici di nuovo più attraenti.)
La tua prima domanda potrebbe essere appena oltre il punto di svolta. Prova a restringere l'intervallo di tempo finché non vedi scansioni solo indice. Non vedrai scansioni di indici (bitmap) con più del 5% circa di tutte le righe qualificate (dipende da molti fattori).
Query
Comunque sia, considera queste query modificate:
SELECT date_part('hour', datelocal) AS hour
, SUM(views) FILTER (WHERE gender = 'male') AS male
, SUM(views) FILTER (WHERE gender = 'female') AS female
FROM reportimpression
WHERE datelocal >= '2019-02-01'
AND datelocal < '2019-03-01' -- '2019-02-28' -- ?
GROUP BY 1
ORDER BY 1;
SELECT date_trunc('day', datelocal) AS day
, SUM(views) FILTER (WHERE gender = 'male') AS male
, SUM(views) FILTER (WHERE gender = 'female') AS female
FROM reportimpressionday
WHERE datelocal >= '2019-02-01'
AND datelocal < '2019-03-01'
GROUP BY 1
ORDER BY 1;
Punti principali
-
Quando si utilizza il formato data localizzato come
'2-1-2019'
, passa ato_timestamp()
con specificatori di formato espliciti. Altrimenti dipende dalle impostazioni locali e potrebbe interrompersi (silenziosamente) quando viene chiamato da una sessione con impostazioni diverse. Piuttosto, usa i formati di data / ora ISO come dimostrato che non dipendono dalle impostazioni locali. -
Sembra che tu voglia includere l'intero mese di febbraio. Ma la tua domanda perde il limite superiore. Per uno, febbraio potrebbe avere 29 giorni. Un
datelocal < '2-28-2019'
esclude anche tutto il 28 febbraio. Usadatelocal < '2019-03-01'
invece. -
È più economico raggruppare e ordinare in base alla stessa espressione come hai fatto in
SELECT
elenca se puoi. Quindi usadate_trunc()
Lì anche. Non usare espressioni diverse senza bisogno. Se hai necessità il datepart nel risultato, applicalo all'espressione raggruppata, come:SELECT date_part('day', date_trunc('day', datelocal)) AS day ... GROUP BY date_trunc('day', datelocal) ORDER BY date_trunc('day', datelocal);
Codice un po' più rumoroso, ma più veloce (e forse anche più facile da ottimizzare per il pianificatore di query).
-
Utilizza l'aggregato
FILTER
clausola in Postgres 9.4 o successivo. È più pulito e un po' più veloce. Vedi: