AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 116705
Accepted
Christian Schmitt
Christian Schmitt
Asked: 2015-10-02 03:12:44 +0800 CST2015-10-02 03:12:44 +0800 CST 2015-10-02 03:12:44 +0800 CST

Como fazer esta consulta usar meu índice de várias colunas?

  • 772

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_addresstem um índice sobre street, zip, city, country, que funciona bem.

postgresql index
  • 2 2 respostas
  • 1089 Views

2 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2015-10-02T03:50:12+08:002015-10-02T03:50:12+08:00

    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:

    
    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);
    

    Este índice cobriria sua consulta:

    CREATE INDEX crm_customer_foo_index ON crm_customer (
      rating
    , lower(name::text) text_pattern_ops
    , address_id);  -- for the join
    

    Para otimizar, sugiro adaptar sua consulta adicionalmente:

    SELECT * 
    FROM   customer_list
    WHERE  lower(name::text) LIKE 'env%' 
    AND    rating = 3 
    ORDER  BY lower(name::text), name
    LIMIT  20;

    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:

    CREATE INDEX crm_customer_foo_index ON crm_customer (
      rating
    , lower(left(name::text, 7)) text_pattern_ops  -- example with local optimum
    , address_id);  -- for the join
    

    A consulta deve corresponder à expressão de índice:

    WHERE  lower(left(name::text, 7)) LIKE 'env%' 
    ...
    ORDER  BY lower(left(name::text, 7)), name
    

    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. Apenas text:

    • Devo adicionar um limite de comprimento arbitrário às colunas VARCHAR?

    Sobre correspondência de padrões e text_pattern_ops:

    • Correspondência de padrões com LIKE, SIMILAR TO ou expressões regulares no PostgreSQL

    Por que coloquei ratingprimeiro no índice?

    • Índice e desempenho de várias colunas

    Existem técnicas de consulta para capitalizar melhor as colunas de índice não principais. Considere o Wiki do Postgres em "Loose indexscan" .

    • 6
  2. dezso
    2015-10-02T03:55:40+08:002015-10-02T03:55:40+08:00

    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.

    1. a falta de algum adequado - na maioria dos casos, isso significa que a primeira coluna de qualquer índice não se encaixa em nenhuma WHEREcondição ou ordem de sua consulta
    2. se a ordem das colunas dificultar a localização das linhas necessárias
    3. se um índice for muito grande (contiver muitas colunas

    (Há possivelmente outros também, mas no nosso caso, essas são as coisas importantes.)

    Vamos reiterar sua consulta:

    SELECT * 
      FROM customer_list
     WHERE lower(name::text) LIKE 'env%' 
           AND rating = 3 
     ORDER BY name ASC
     LIMIT 20 
     OFFSET 0;
    

    O que é, efetivamente, seria traduzido no seguinte:

    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
     WHERE lower(name::text) LIKE 'env%' 
           AND rating = 3 
     ORDER BY name ASC
     LIMIT 20 
     OFFSET 0;
    

    A WHEREclá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ão ratingé 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:

    CREATE INDEX ON crm_customer ((lower(name::text) text_pattern_ops, rating, address_id); -- thanks, Erwin
    

    há chances de ser usado. Ajuda a encontrar as linhas em crm_customer, depois dá o address_idpara usar na junção.

    Se você puder mudar um pouco sua lógica e fazer uma

    ORDER BY lower(name)
    

    essa ordenação também seria facilitada pelo mesmo índice - nesse caso, você perderia a distinção entre valores como 'ambiente' e 'Ambiente'.

    Notas:

    • Sugiro que você leia as excelentes páginas Use The Index, Lucas . Ele fornece uma introdução agradável e completa ao uso de índices.
    • Não tive o cuidado de incluir a junção em minha definição de índice. A excelente resposta de Erwin me fez perceber minha falha - agora editei, mas a fonte não é minha.
    • 3

relate perguntas

  • Quanto "Padding" coloco em meus índices?

  • Sequências Biológicas do UniProt no PostgreSQL

  • O que significa "índice" em RDBMSs? [fechado]

  • Como criar um índice condicional no MySQL?

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve