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 / 9043
Accepted
Bruno
Bruno
Asked: 2011-12-12 11:38:13 +0800 CST2011-12-12 11:38:13 +0800 CST 2011-12-12 11:38:13 +0800 CST

PostgreSQL: reutilização de resultado intermediário complexo na mesma consulta

  • 772

Usando o PostgreSQL (8.4), estou criando uma visão que resume vários resultados de algumas tabelas (por exemplo, criando colunas a, b, cna visão) e, em seguida, preciso combinar alguns desses resultados na mesma consulta (por exemplo a+b, a-b, , (a+b)/c, ...), de modo a produzir os resultados finais. O que estou percebendo é que os resultados intermediários são totalmente computados cada vez que são usados, mesmo que seja feito dentro da mesma consulta.

Existe uma maneira de otimizar isso para evitar que os mesmos resultados sejam computados todas as vezes?

Aqui está um exemplo simplificado que reproduz o problema.

CREATE TABLE test1 (
    id SERIAL PRIMARY KEY,
    log_timestamp TIMESTAMP NOT NULL
);
CREATE TABLE test2 (
    test1_id INTEGER NOT NULL REFERENCES test1(id),
    category VARCHAR(10) NOT NULL,
    col1 INTEGER,
    col2 INTEGER
);
CREATE INDEX test_category_idx ON test2(category);

-- Added after edit to this question
CREATE INDEX test_id_idx ON test2(test1_id);

-- Populating with test data.
INSERT INTO test1(log_timestamp)
    SELECT * FROM generate_series('2011-01-01'::timestamp, '2012-01-01'::timestamp, '1 hour');
INSERT INTO test2
    SELECT id, substr(upper(md5(random()::TEXT)), 1, 1),
               (20000*random()-10000)::int, (3000*random()-200)::int FROM test1;
INSERT INTO test2
    SELECT id, substr(upper(md5(random()::TEXT)), 1, 1),
               (2000*random()-1000)::int, (3000*random()-200)::int FROM test1;
INSERT INTO test2
    SELECT id, substr(upper(md5(random()::TEXT)), 1, 1),
               (2000*random()-40)::int, (3000*random()-200)::int FROM test1;

Aqui está uma exibição que executa as operações mais demoradas:

CREATE VIEW testview1 AS
    SELECT
       t1.id,
       t1.log_timestamp,
       (SELECT SUM(t2.col1) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='A') AS a,
       (SELECT SUM(t2.col2) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='B') AS b,
       (SELECT SUM(t2.col1 - t2.col2) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='C') AS c
    FROM test1 t1;
  • SELECT a FROM testview1produz este plano (via EXPLAIN ANALYZE):

     Seq Scan on test1 t1  (cost=0.00..1787086.55 rows=8761 width=4) (actual time=12.877..10517.575 rows=8761 loops=1)
       SubPlan 1
         ->  Aggregate  (cost=203.96..203.97 rows=1 width=4) (actual time=1.193..1.193 rows=1 loops=8761)
               ->  Bitmap Heap Scan on test2 t2  (cost=36.49..203.95 rows=1 width=4) (actual time=1.109..1.177 rows=0 loops=8761)
                     Recheck Cond: ((category)::text = 'A'::text)
                     Filter: (test1_id = $0)
                     ->  Bitmap Index Scan on test_category_idx  (cost=0.00..36.49 rows=1631 width=0) (actual time=0.414..0.414 rows=1631 loops=8761)
                           Index Cond: ((category)::text = 'A'::text)
     Total runtime: 10522.346 ms
    

  • SELECT a, a FROM testview1produz este plano :

     Seq Scan on test1 t1  (cost=0.00..3574037.50 rows=8761 width=4) (actual time=3.343..20550.817 rows=8761 loops=1)
       SubPlan 1
         ->  Aggregate  (cost=203.96..203.97 rows=1 width=4) (actual time=1.183..1.183 rows=1 loops=8761)
               ->  Bitmap Heap Scan on test2 t2  (cost=36.49..203.95 rows=1 width=4) (actual time=1.100..1.166 rows=0 loops=8761)
                     Recheck Cond: ((category)::text = 'A'::text)
                     Filter: (test1_id = $0)
                     ->  Bitmap Index Scan on test_category_idx  (cost=0.00..36.49 rows=1631 width=0) (actual time=0.418..0.418 rows=1631 loops=8761)
                           Index Cond: ((category)::text = 'A'::text)
       SubPlan 2
         ->  Aggregate  (cost=203.96..203.97 rows=1 width=4) (actual time=1.154..1.154 rows=1 loops=8761)
               ->  Bitmap Heap Scan on test2 t2  (cost=36.49..203.95 rows=1 width=4) (actual time=1.083..1.143 rows=0 loops=8761)
                     Recheck Cond: ((category)::text = 'A'::text)
                     Filter: (test1_id = $0)
                     ->  Bitmap Index Scan on test_category_idx  (cost=0.00..36.49 rows=1631 width=0) (actual time=0.426..0.426 rows=1631 loops=8761)
                           Index Cond: ((category)::text = 'A'::text)
     Total runtime: 20557.581 ms
    

Aqui, selecionar a, aleva o dobro do tempo que selecionar a, enquanto na verdade eles podem ser calculados apenas uma vez. Por exemplo, com SELECT a, a+b, a-b FROM testview1, ele percorre o subplano a3 vezes e bduas vezes, enquanto o tempo de execução pode ser reduzido para 2/5 do tempo total (assumindo que + e - são insignificantes aqui).

É bom que ele não calcule as colunas não utilizadas ( be c) quando elas não são necessárias, mas existe uma maneira de fazê-lo calcular as mesmas colunas usadas da exibição apenas uma vez?

EDIT: @Frank Heikens sugeriu corretamente o uso de um índice, que estava faltando no exemplo acima. Embora melhore a velocidade de cada subplano, não impede que a mesma subconsulta seja computada várias vezes. Desculpe, eu deveria ter colocado isso na pergunta inicial para deixar claro.

postgresql performance
  • 2 2 respostas
  • 4273 Views

2 respostas

  • Voted
  1. Best Answer
    Bruno
    2011-12-13T02:22:33+08:002011-12-13T02:22:33+08:00

    (Peço desculpas por responder à minha própria pergunta, mas depois de ler esta pergunta e resposta não relacionadas , ocorreu-me que deveria tentar usar um CTE. Funciona.)

    Aqui está outra visão, semelhante à testview1da pergunta, mas que usa uma expressão de tabela comum :

    CREATE VIEW testview2 AS
        WITH testcte AS (SELECT
           t1.id,
           t1.log_timestamp,
           (SELECT SUM(t2.col1) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='A') AS a,
           (SELECT SUM(t2.col2) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='B') AS b,
           (SELECT SUM(t2.col1 - t2.col2) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='C') AS c
          FROM test1 t1)
        SELECT * FROM testcte;
    

    (Este é apenas um exemplo, não estou sugerindo que combinar uma exibição e um CTE seja necessariamente uma boa ideia: um CTE pode ser suficiente.)

    Ao contrário testview1de , o plano de consulta por SELECT a FROM testview2enquanto também calcula be c, que foram ignorados desde então em testview1:

    Subquery Scan testview2  (cost=395272.42..395535.25 rows=8761 width=8) (actual time=0.256..607.941 rows=8761 loops=1)
       ->  CTE Scan on testcte  (cost=395272.42..395447.64 rows=8761 width=36) (actual time=0.255..604.106 rows=8761 loops=1)
             CTE testcte
               ->  Seq Scan on test1 t1  (cost=0.00..395272.42 rows=8761 width=12) (actual time=0.252..589.358 rows=8761 loops=1)
                     SubPlan 1
                       ->  Aggregate  (cost=15.02..15.03 rows=1 width=4) (actual time=0.021..0.021 rows=1 loops=8761)
                             ->  Bitmap Heap Scan on test2 t2  (cost=4.28..15.02 rows=1 width=4) (actual time=0.015..0.015 rows=0 loops=8761)
                                   Recheck Cond: (test1_id = $0)
                                   Filter: ((category)::text = 'A'::text)
                                   ->  Bitmap Index Scan on test_if_idx  (cost=0.00..4.28 rows=3 width=0) (actual time=0.009..0.009 rows=3 loops=8761)
                                         Index Cond: (test1_id = $0)
                     SubPlan 2
                       ->  Aggregate  (cost=15.02..15.03 rows=1 width=4) (actual time=0.019..0.019 rows=1 loops=8761)
                             ->  Bitmap Heap Scan on test2 t2  (cost=4.28..15.02 rows=1 width=4) (actual time=0.012..0.012 rows=0 loops=8761)
                                   Recheck Cond: (test1_id = $0)
                                   Filter: ((category)::text = 'B'::text)
                                   ->  Bitmap Index Scan on test_if_idx  (cost=0.00..4.28 rows=3 width=0) (actual time=0.007..0.007 rows=3 loops=8761)
                                         Index Cond: (test1_id = $0)
                     SubPlan 3
                       ->  Aggregate  (cost=15.02..15.04 rows=1 width=8) (actual time=0.020..0.020 rows=1 loops=8761)
                             ->  Bitmap Heap Scan on test2 t2  (cost=4.28..15.02 rows=1 width=8) (actual time=0.013..0.014 rows=0 loops=8761)
                                   Recheck Cond: (test1_id = $0)
                                   Filter: ((category)::text = 'C'::text)
                                   ->  Bitmap Index Scan on test_if_idx  (cost=0.00..4.28 rows=3 width=0) (actual time=0.007..0.007 rows=3 loops=8761)
                                         Index Cond: (test1_id = $0)
    

    No entanto, ele não recalcula os resultados que são usados ​​várias vezes na mesma consulta (que era o objetivo).

    Ao contrário testview1de which SELECT a, a, a, a, alevou 5 vezes mais do que SELECT a, aqui SELECT a, a, a, a, a, b, c, a+b, a+c, b+c FROM testview2leva tanto tempo quanto SELECT a FROM testview2ou SELECT a, b, c FROM testview2. Ele só passa por a, be cuma vez:

     Subquery Scan testview2  (cost=395272.42..395600.96 rows=8761 width=24) (actual time=0.147..562.790 rows=8761 loops=1)
       ->  CTE Scan on testcte  (cost=395272.42..395447.64 rows=8761 width=36) (actual time=0.144..554.194 rows=8761 loops=1)
             CTE testcte
               ->  Seq Scan on test1 t1  (cost=0.00..395272.42 rows=8761 width=12) (actual time=0.140..542.657 rows=8761 loops=1)
                     SubPlan 1
                       ->  Aggregate  (cost=15.02..15.03 rows=1 width=4) (actual time=0.019..0.019 rows=1 loops=8761)
                             ->  Bitmap Heap Scan on test2 t2  (cost=4.28..15.02 rows=1 width=4) (actual time=0.012..0.013 rows=0 loops=8761)
                                   Recheck Cond: (test1_id = $0)
                                   Filter: ((category)::text = 'A'::text)
                                   ->  Bitmap Index Scan on test_if_idx  (cost=0.00..4.28 rows=3 width=0) (actual time=0.007..0.007 rows=3 loops=8761)
                                         Index Cond: (test1_id = $0)
                     SubPlan 2
                       ->  Aggregate  (cost=15.02..15.03 rows=1 width=4) (actual time=0.019..0.019 rows=1 loops=8761)
                             ->  Bitmap Heap Scan on test2 t2  (cost=4.28..15.02 rows=1 width=4) (actual time=0.012..0.012 rows=0 loops=8761)
                                   Recheck Cond: (test1_id = $0)
                                   Filter: ((category)::text = 'B'::text)
                                   ->  Bitmap Index Scan on test_if_idx  (cost=0.00..4.28 rows=3 width=0) (actual time=0.006..0.006 rows=3 loops=8761)
                                         Index Cond: (test1_id = $0)
                     SubPlan 3
                       ->  Aggregate  (cost=15.02..15.04 rows=1 width=8) (actual time=0.018..0.019 rows=1 loops=8761)
                             ->  Bitmap Heap Scan on test2 t2  (cost=4.28..15.02 rows=1 width=8) (actual time=0.012..0.012 rows=0 loops=8761)
                                   Recheck Cond: (test1_id = $0)
                                   Filter: ((category)::text = 'C'::text)
                                   ->  Bitmap Index Scan on test_if_idx  (cost=0.00..4.28 rows=3 width=0) (actual time=0.007..0.007 rows=3 loops=8761)
                                         Index Cond: (test1_id = $0)
    
    • 6
  2. Frank Heikens
    2011-12-12T12:19:06+08:002011-12-12T12:19:06+08:00

    Você precisa de um índice em test1_id na tabela test2, isso mudará as coisas.

    Seq Scan on test1 t1  (cost=0.00..301450.63 rows=8761 width=12) (actual time=0.108..229.859 rows=8761 loops=1)
      SubPlan 1
        ->  Aggregate  (cost=11.45..11.46 rows=1 width=4) (actual time=0.008..0.008 rows=1 loops=8761)
              ->  Bitmap Heap Scan on test2 t2  (cost=3.27..11.45 rows=1 width=4) (actual time=0.007..0.007 rows=0 loops=8761)
                    Recheck Cond: (test1_id = t1.id)
                    Filter: ((category)::text = 'A'::text)
                    ->  Bitmap Index Scan on idx_id  (cost=0.00..3.27 rows=3 width=0) (actual time=0.003..0.003 rows=3 loops=8761)
                          Index Cond: (test1_id = t1.id)
      SubPlan 2
        ->  Aggregate  (cost=11.45..11.46 rows=1 width=4) (actual time=0.007..0.008 rows=1 loops=8761)
              ->  Bitmap Heap Scan on test2 t2  (cost=3.27..11.45 rows=1 width=4) (actual time=0.006..0.006 rows=0 loops=8761)
                    Recheck Cond: (test1_id = t1.id)
                    Filter: ((category)::text = 'B'::text)
                    ->  Bitmap Index Scan on idx_id  (cost=0.00..3.27 rows=3 width=0) (actual time=0.003..0.003 rows=3 loops=8761)
                          Index Cond: (test1_id = t1.id)
      SubPlan 3
        ->  Aggregate  (cost=11.46..11.47 rows=1 width=8) (actual time=0.008..0.008 rows=1 loops=8761)
              ->  Bitmap Heap Scan on test2 t2  (cost=3.27..11.45 rows=1 width=8) (actual time=0.006..0.006 rows=0 loops=8761)
                    Recheck Cond: (test1_id = t1.id)
                    Filter: ((category)::text = 'C'::text)
                    ->  Bitmap Index Scan on idx_id  (cost=0.00..3.27 rows=3 width=0) (actual time=0.003..0.003 rows=3 loops=8761)
                          Index Cond: (test1_id = t1.id)
    Total runtime: 232.419 ms
    
    • 3

relate perguntas

  • Sequências Biológicas do UniProt no PostgreSQL

  • Como determinar se um Índice é necessário ou necessário

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • 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

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

    • 4 respostas
  • Marko Smith

    Como você mostra o SQL em execução em um banco de dados Oracle?

    • 2 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

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

    • 10 respostas
  • Marko Smith

    Posso ver Consultas Históricas executadas em um banco de dados SQL Server?

    • 6 respostas
  • Marko Smith

    Como uso currval() no PostgreSQL para obter o último id inserido?

    • 10 respostas
  • Marko Smith

    Como executar o psql no Mac OS X?

    • 11 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
  • Marko Smith

    Passando parâmetros de array para um procedimento armazenado

    • 12 respostas
  • Martin Hope
    Manuel Leduc Restrição exclusiva de várias colunas do PostgreSQL e valores NULL 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler Quando uma chave primária deve ser declarada sem cluster? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +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
  • Martin Hope
    BrunoLM Guid vs INT - Qual é melhor como chave primária? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick Como posso otimizar um mysqldump de um banco de dados grande? 2011-01-04 13:13:48 +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