Eu tenho uma tabela enorme no Postgresql-11 da seguinte forma:
CREATE TABLE my_huge_table(
tick_time timestamp(6) with time zone NOT NULL,
brok_time timestamp(6) with time zone,
trade_day date NOT NULL,
--other fields ...
...
CONSTRAINT my_huge_table_pkey PRIMARY KEY (tick_time)
);
CREATE INDEX idx_my_huge_table_td_time ON my_huge_table USING brin
( trade_day, abs(tick_time - brok_time) );
Aí eu faço uma query e quero que ela aproveite o index idx_my_huge_table_td_time
, assim:
SELECT * FROM my_huge_table
WHERE trade_day BETWEEN TO_DATE('20220104', 'YYYYMMDD') AND TO_DATE('20220104', 'YYYYMMDD')
AND ABS(tick_time - brok_time) < INTERVAL '10 s';
Mas o PostgreSQL se recusou a executá-lo e disse:
ERRO: a função abs(intervalo) não existe
LINHA 3: AND ABS(tick_time - brok_time) < INTERVAL '10 s'
^
DICA: Nenhuma função corresponde ao nome e aos tipos de argumento fornecidos. Pode ser necessário adicionar conversões de tipo explícito.
Estado SQL: 42883 Caractere: 525
Parece que o func NÃOabs()
pode aceitar um valor de intervalo como argumento.
Então, mudei minha consulta:
SELECT * FROM my_huge_table
WHERE trade_day BETWEEN TO_DATE('20220104', 'YYYYMMDD') AND TO_DATE('20220104', 'YYYYMMDD')
AND GREATEST(tick_time - brok_time, brok_time - tick_time) < INTERVAL '10 s';
Desta vez pode ser executado, mas não aproveitou o índice.
Minhas perguntas:
1.Como devo compor a expressão do índice? Na verdade, quero registrar uma distância (valor de intervalo absoluto) entre dois campos de carimbo de data/hora;
2.Como devo codificar a consulta que pode usar o índice acima?
3.Na verdade , NÃOGREATEST(tick_time - brok_time, brok_time - tick_time)
é uma boa ideia, pois invocou a computação duas vezes. Não é?
4.Após criado o índice, observo que a DDL SQL real do índice reportado pelo PostgreSQL é:
CREATE INDEX idx_my_huge_table_td_time ON public.my_huge_table USING brin
(trade_day, abs(date_part('epoch'::text, tick_time - brok_time)));
O valor da expressão foi convertido em um text
tipo? Aparentemente NÃO é minha expectativa!
A resposta é criar uma coluna gerada da seguinte maneira (todo o código abaixo está disponível no violino aqui ):
Eu tinha uma resposta original (mostrada no final da resposta), mas a revisei para usar uma
Generated Column
(também conhecida como coluna "Computada" ou "Virtual") em vez de umaExpression Index
(também conhecida como "Índice Funcional").Isto tem as vantagens de:
a) É calculado na inserção e não precisa ser recalculado toda vez e
b) torna o SQL muito mais claro - veja a resposta original abaixo.
Há uma desvantagem em que ele usa mais espaço, mas descobri que isso normalmente não é um problema crítico (nunca vi isso pessoalmente). Infelizmente, o PostgreSQL ainda não possui colunas virtuais geradas - veja o link.
A definição da sua tabela deve ser a seguinte:
Em seguida, crie um índice em
abs_b_minus_t
:Preencher:
Então corremos:
Resultado:
Então, vemos que está funcionando - estamos obtendo valores absolutos da diferença entre
broktime
etradtime
.Agora, podemos verificar o uso do índice - executamos
SET enable_seqscan = OFF;
e então:Resultado:
Então, estamos usando
t_ix
com o índice BRIN em nosso campo gerado.Resposta original:
Agora, criamos nosso índice funcional da seguinte maneira:
Preencha a tabela:
Agora testamos:
Resultado:
Assim, temos os valores e seus absolutos.
Resultado:
Para verificar o uso do índice, desabilitamos os seqscans:
Em seguida, executamos:
Resultado:
Então, vemos que
t_ix
é usado com o bitmap relativamente eficiente