Atualmente, tenho uma visão que é definida assim:
View "public.customer_list"
Column | Type | Modifiers | Storage | Description
-----------+-------------------------+-----------+----------+-------------
id | bigint | | plain |
name | character varying(100) | | extended |
street | character varying(100) | | extended |
zip | character varying(10) | | extended |
city | character varying(100) | | extended |
country | character varying(3) | | extended |
phone | character varying(100) | | extended |
mail | character varying(100) | | extended |
rating | integer | | plain |
salesnote | character varying(1800) | | extended |
View definition:
SELECT c.id,
c.name,
a.street,
a.zip,
a.city,
a.country,
c.phone,
c.mail,
c.rating,
c.salesnote
FROM crm_customer c
JOIN crm_address a ON a.id = c.address_id;
Para melhor pesquisar na Lista criei um índice com o seguinte:
CREATE INDEX crm_customer_big_index
ON crm_customer (name ASC,
name text_pattern_ops,
(id::text) text_pattern_ops,
phone text_pattern_ops,
mail text_pattern_ops,
rating);
Eu pesquiso através desta tabela com uma consulta variável onde, na maioria das vezes, se parece com isso:
SELECT *
FROM customer_list
WHERE lower(name::text) LIKE 'env%'
AND rating = 3
ORDER BY name ASC
LIMIT 20
OFFSET 0;
Mas ainda assim meu analisador nunca usará um índice. Existe uma maneira de usar um?
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=164.04..164.08 rows=15 width=129) (actual time=3.243..3.244 rows=1 loops=1)
-> Sort (cost=164.04..164.08 rows=15 width=129) (actual time=3.243..3.243 rows=1 loops=1)
Sort Key: c.name
Sort Method: quicksort Memory: 26kB
-> Nested Loop (cost=0.28..163.75 rows=15 width=129) (actual time=0.020..3.225 rows=1 loops=1)
-> Seq Scan on crm_customer c (cost=0.00..111.13 rows=15 width=98) (actual time=0.012..3.216 rows=1 loops=1)
Filter: ((rating = 3) AND (lower((name)::text) ~~ 'env%'::text))
Rows Removed by Filter: 2978
-> Index Only Scan using crm_address_search_index on crm_address a (cost=0.28..3.50 rows=1 width=47) (actual time=0.006..0.006 rows=1 loops=1)
Index Cond: (id = c.address_id)
Heap Fetches: 0
Planning time: 0.580 ms
Execution time: 3.286 ms
Atualmente, a consulta ainda é "um tanto" rápida. No entanto, eu poderia ter muito mais dados dentro da minha tabela. Além disso, crm_address
tem um índice sobre street, zip, city, country
, que funciona bem.
A expressão
lower(name::text)
não está coberta pelo seu índice, o que também não parece ser nada útil , pelo menos pelo que vemos na sua pergunta:Este índice cobriria sua consulta:
Para otimizar, sugiro adaptar sua consulta adicionalmente:
Além disso, se você estiver interessado apenas nos primeiros caracteres de
name character varying(100)
, torne o índice mais curto e rápido indexando apenas os primeiros caracteres:A consulta deve corresponder à expressão de índice:
Não tenho certeza se a visão está atrapalhando de alguma forma. Mas deve ser transparente.
Eu não usaria
varchar(n)
de jeito nenhum. Apenastext
:Sobre correspondência de padrões e
text_pattern_ops
:Por que coloquei
rating
primeiro no índice?Existem técnicas de consulta para capitalizar melhor as colunas de índice não principais. Considere o Wiki do Postgres em "Loose indexscan" .
O planejador/otimizador escolherá usar um índice se (e somente se) as estimativas de custo sugerirem que a consulta seria executada mais rapidamente/mais barata. (Existem muitas simplificações na frase anterior, mas essa é a essência.) Isso significa que o índice deve suportar a consulta - fornecer uma maneira mais rápida de encontrar as linhas necessárias, de ordenar ou outras operações.
Ao mesmo tempo, ler o índice é basicamente a mesma operação que ler uma tabela. Agora, se o planejador descobrir que precisa ler o índice e, em seguida, ir para a tabela para buscar as linhas, ele pode decidir que isso é muito caro e usaria uma varredura sequencial.
Existem fatores que apontam para a não utilização de um índice.
WHERE
condição ou ordem de sua consulta(Há possivelmente outros também, mas no nosso caso, essas são as coisas importantes.)
Vamos reiterar sua consulta:
O que é, efetivamente, seria traduzido no seguinte:
A
WHERE
cláusula precisa localizar as linhas parcialmente com base em uma expressão. Seu índice não possui essa expressão. Este é um caso 1 na lista acima. Entãorating
é a última coluna - a classificação fornecida é tão difícil de encontrar no índice quanto possível. Aqui nos deparamos com o caso 2. E, no geral, seu índice pode ser quase tão grande quanto sua tabela (sem ver a definição da tabela, isso é apenas um palpite) - é simplesmente muito caro para usar.Se você criar o seguinte índice:
há chances de ser usado. Ajuda a encontrar as linhas em
crm_customer
, depois dá oaddress_id
para usar na junção.Se você puder mudar um pouco sua lógica e fazer uma
essa ordenação também seria facilitada pelo mesmo índice - nesse caso, você perderia a distinção entre valores como 'ambiente' e 'Ambiente'.
Notas: