Tenho uma tabela enorme com um índice composto em (A, B, C)
.
-- psql (13.16 (Debian 13.16-0+deb11u1), server 14.12)
\d index_a_b_c
Index "public.index_a_b_c"
Column | Type | Key? |
----------+-----------------------+------+
A | character varying(44) | yes |
B | numeric(20,0) | yes |
C | numeric(20,0) | yes |
btree, for table "public.table_a_b_c"
Preciso de todos B
os s distintos.
Esta consulta é executada com Index Only Scan
, mas, verifica todas as A
correspondências. O que não é escala para o meu caso, pois para alguns A
s há milhões de linhas. Milhões de Index Only Scan
linhas são lentos.
EXPLAIN (ANALYZE true)
SELECT DISTINCT ON ("B") "B"
FROM "table_a_b_c"
WHERE "A" = 'astring'
-- Execution time: 0.172993s
-- Unique (cost=0.83..105067.18 rows=1123 width=5) (actual time=0.037..19.468 rows=67 loops=1)
-- -> Index Only Scan using index_a_b_c on table_a_b_c (cost=0.83..104684.36 rows=153129 width=5) (actual time=0.036..19.209 rows=1702 loops=1)
-- Index Cond: (A = 'astring'::text)
-- Heap Fetches: 351
-- Planning Time: 0.091 ms
-- Execution Time: 19.499 ms
Como você pode ver, ele executa mais de 1,7 mil linhas, filtra manualmente e retorna 67 linhas. 20 ms, obtendo dezenas de segundos quando 1,7 mil chega a milhões.
Também preciso de todos os maiores C
s para B
s distintos.
Mesma coisa que em 1) . Em teoria, o Postgres poderia conhecer possíveis B
s, e não precisaria verificar toda a lista correspondida a A
.
EXPLAIN (ANALYZE true)
SELECT DISTINCT ON ("B") *
FROM "table_a_b_c"
WHERE "A" = 'astring'
ORDER BY "B" DESC,
"C" DESC
-- Execution time: 0.822705s
-- Unique (cost=0.83..621264.51 rows=1123 width=247) (actual time=0.957..665.927 rows=67 loops=1)
-- -> Index Scan using index_a_b_c on table_a_b_c (cost=0.83..620881.69 rows=153130 width=247) (actual time=0.955..664.408 rows=1702 loops=1)
-- Index Cond: (a = 'astring'::text)
-- Planning Time: 0.116 ms
-- Execution Time: 665.978 ms
Mas, por exemplo, isto é rápido:
SELECT * WHERE A="x" AND B=1 ORDER BY C DESC
UNION
SELECT * WHERE A="x" AND B=2 ORDER BY C DESC
UNION
....
para todos B
os s possíveis. É como um loop com número de B
tempo.
Questões
a) O índice on não deveria (A, B, C)
ser um superconjunto de (A, B)
em teoria? (A, B)
será super rápido para distinct.
b) Por que é difícil encontrar B
s distintos para o Postgres?
c) Como lidar com isso sem um novo índice?
Anexo A (da sua descrição):
Anexo B (do plano de consulta):
Significa muitas linhas por valor distinto em
B
.Emular varredura de índice-pulo para muitas linhas por grupo
Apenas distinto
B
:Distinto
B
com o maiorC
:violino
Agora, as varreduras somente de índice buscam apenas os valores necessários. (Nenhuma varredura sequencial do índice de cobertura.) Efetivamente, você obtém o que tentou forçar com a
UNION
consulta (o que deveria serUNION ALL
o caso).Deve ser muito rápido .
Relacionado:
Há detalhes intrincados nessa técnica. Veja:
Sobre a ordem de classificação alternada (
ASC
/DESC
) em um índice multicolunas:DISTINCT ON
para apenas algumas linhas por grupoAs consultas que você tentou são as ótimas nesse caso. A sobrecarga considerável para o CTE recursivo não compensaria.
violino
Ver: