O PostgreSQL pode usar valores nulos em seus índices?

10

Eu tenho lido este livro que diz que

O banco de dados pressupõe que Indexed_Col NÃO É NULL cobre um intervalo muito grande para ser útil, portanto, o banco de dados não direcionará para um índice dessa condição.

Reconheço que o livro tem mais de 10 anos, mas já se mostrou bastante útil - Usando as instruções recolhidas em suas páginas, eu acelerava uma consulta por um fator de dez.

Além disso, ao executar EXPLAIN ANALYZEuma SELECTconsulta, descobri que nenhum dos meus índices está sendo usado, mesmo quando por todos os direitos eles deveriam estar.

Assim, minha pergunta é:

Supondo que exista uma tabela que tenha uma coluna, cuja definição da coluna inclua "NOT NULL" e que exista um índice que cubra essa coluna, esse índice seria usado em uma consulta dessa tabela em que as colunas fazem parte da consulta?

Gostar:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;
FuriousFolder
fonte

Respostas:

9

O PostgreSQL certamente pode usar um índice para IS NOT NULL. Também não vejo suposições do planejador de consultas sobre essa condição.

Se a fração nula da coluna ( pg_statistic.stanullfrac) for baixa o suficiente para sugerir que o índice é útil para a consulta, o PostgreSQL utilizará um índice.

Não consigo descobrir o que você está tentando dizer com:

Se isso estiver correto, meu entendimento é de que um índice em uma coluna definida como "NOT NULL" não seja usado em uma consulta que use essa coluna?

Certamente, um índice não será usado para uma IS NOT NULLcondição em uma NOT NULLcoluna. Sempre corresponderia a 100% das linhas, portanto, um seqscan quase sempre será muito mais rápido.

O PostgreSQL não usará um índice se o índice não filtrar uma grande proporção de linhas para uma consulta. A única exceção provável é quando você solicita um conjunto de colunas cobertas por um único índice, em uma ordem que corresponde ao índice. O PostgreSQL pode fazer uma varredura apenas de índice. Por exemplo, se houver um índice t(a, b, c)e você:

select a, b FROM t ORDER BY a, b, c;

O PostgreSQL pode usar seu índice, mesmo que nenhuma linha seja filtrada, porque ele só precisa ler o índice e pode pular a leitura da pilha, evitar fazer uma classificação etc.

Craig Ringer
fonte
Tudo isso é verdade a partir de PG 9.0
eradman
11
E mesmo em uma coluna anulável, uma consulta com condição WHERE column IS NOT NULLpode não usar o índice porque, como diz o livro: "cobre um intervalo muito grande para ser útil". Se 90% dos valores não forem nulos, um seqscan provavelmente também será mais rápido.
precisa saber é o seguinte
Exatamente. Pode, mas apenas se uma grande fração da tabela for nula. Freqüentemente, nesse caso, um índice parcial é uma opção melhor de qualquer maneira.
Craig Ringer
Sim. Eu estava tentando dizer que (como eu a entendo) a parte "abrange um intervalo muito grande" refere-se ao índice, mas em relação à condição específica e não ao índice em geral.
precisa saber é o seguinte
2
@FuriousFolder Heh, há muitas negações aqui. O PostgreSQL não usará um índice em uma NOT NULLcoluna para uma IS NOT NULLconsulta, a menos que esse índice também seja útil para outras partes da WHEREcláusula, junte filtros, etc., ou seja utilizável para uma varredura ordenada apenas de índice. Em outras palavras, ele ignorará completamente o redundante IS NOT NULLna NOT NULLcoluna e fará escolhas de uso do índice com base em outros detalhes. (Consulte editar, reexaminar apenas o índice).
Craig Ringer
2

Além da resposta completa de Craig, gostaria de acrescentar que a capa do livro que você menciona diz:

Abrange Oracle, DB2 e SQL Server

Portanto, não confio que seja uma ótima fonte de conselhos sobre o PostgreSQL em particular. Cada RDBMS pode ser surpreendentemente diferente!

Estou um pouco confuso com sua pergunta original, mas aqui está um exemplo que mostra que a seção do livro não está 100% correta. Para evitar mais confusões, veja o parágrafo relevante inteiro, você pode vê-lo na Pesquisa de Livros do Google .

O banco de dados pressupõe que Indexed_Col NÃO É NULL cobre um intervalo muito grande para ser útil, portanto, o banco de dados não direcionará para um índice dessa condição. Em casos raros, ter qualquer valor não nulo é tão raro que uma varredura de intervalo de índice sobre todos os possíveis valores nulos é benéfica. Nesses casos, se você descobrir um limite inferior ou superior seguro para o intervalo de todos os valores possíveis, poderá ativar uma verificação de intervalo com uma condição como Positive_ID_Column> -1 ou Date_Column> TO_DATE ('0001/01/01' , 'AAAA / MM / DD').

O Postgres pode, na verdade (no caso planejado a seguir), usar um índice para atender a IS NOT NULLconsultas sem adicionar kludges de varredura de intervalo como o sugerido Positive_ID_Column > -1. Veja os comentários nas perguntas de Craig sobre por que o Postgres está escolhendo esse índice nesse caso específico e a observação sobre o uso de índices parciais.

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

A propósito, este é o Postgres 9.3, mas acredito que os resultados seriam aproximadamente similares no 9.1, embora não use uma "Index Only Scan".

Edit: Vejo que você esclareceu sua pergunta original e, aparentemente, está se perguntando por que o Postgres não está usando um índice em um exemplo simples como:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

Provavelmente porque você não possui nenhuma linha na tabela. Então adicione alguns dados de teste e ANALYZE my_table;.

Josh Kupershmidt
fonte
Na descrição desse livro (ênfase minha): "O autor Dan Tow descreve um método que economiza tempo que ele desenvolveu para encontrar o plano de execução ideal - rápida e sistematicamente - independentemente da complexidade do SQL ou da plataforma de banco de dados que está sendo usada ". talvez você tenha esquecido o nº 1 da pergunta, a saber, que a coluna é definida como NOT NULL, não que a consulta use IS NOT NULLcomo condição de índice. Isso está nos comentários que você referenciou, mas atualizamos a pergunta para incluí-la.
FuriousFolder
Além disso, o livro em si é agnóstico língua: as únicas partes específicas do DMBs está prestes mostrando planos de consulta, que Postgres torna bastante simples :)
FuriousFolder
11
@FuriousFolder a coluna é definida como NOT NULL, mas esta parte (na sua pergunta, do livro): "que Indexed_Col NÃO É NULL abrange ..." está se referindo à condição where e não à definição da coluna. Embora seja difícil ter certeza, porque está fora de contexto. Talvez você deva incluir o parágrafo inteiro (anterior) do livro.
precisa saber é o seguinte
-1

Você não postou sua consulta ou dados de exemplo. Mas a razão mais comum pela qual os índices não são usados ​​tem a ver com volume.

Os índices são como uma lista telefônica que traduz uma coluna em um local de linha. Se você estiver procurando apenas algumas linhas, faz sentido procurar cada linha na lista telefônica e, em seguida, procurar a linha na tabela principal.

Mas por mais de algumas linhas, é mais barato pular a lista telefônica e iterar em todas as linhas da tabela principal. Na minha experiência, o ponto de inflexão é de cerca de 100 linhas.

Andomar
fonte
"Os índices são como uma lista telefônica que traduz uma coluna em um local da linha. Se você está procurando apenas algumas linhas, faz sentido procurar cada linha na lista telefônica e depois procurar a linha na tabela principal." Na verdade, os índices são como listas telefônicas menores, que são atualizadas sempre que a lista telefônica que eles indexam é atualizada. Você sabe que sempre que abrir uma lista telefônica menor, encontrará toda e qualquer informação descrita por sua condição de indexação. Por exemplo, todas as pessoas com o nome 'Frank' em uma tabela de índice: CREATE INDEX ix_frank ON people(name) WHERE name ='frank'.
FuriousFolder
Isso permite que uma varredura apenas de índice seja muito mais rápida, pois você pode ler toda a "lista telefônica menor" na memória, o que não é possível com uma tabela de vários milhões de linhas.
FuriousFolder
@FuriousFolder: você está descrevendo uma verificação apenas de índice. Mas o OP diz que seus índices não estão sendo usados, o que não aconteceria se uma verificação apenas de índice satisfizesse a consulta.
Andomar 12/08/2015
Andomar ... eu sou o OP, haha. Meu objetivo é exatamente isso; para fazer com que essa consulta use uma verificação apenas de índice. Tenho desde conseguido isso, já que Craig explicou que postgres é capaz de usar um índice em uma coluna onde a definição da coluna inclui NOT NULL
FuriousFolder