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 / 3492
Accepted
Bruno
Bruno
Asked: 2011-06-27 07:33:27 +0800 CST2011-06-27 07:33:27 +0800 CST 2011-06-27 07:33:27 +0800 CST

Otimizando a consulta usando a exibição na estrutura do EAV

  • 772

Um aplicativo está gravando em um banco de dados que segue uma estrutura EAV, semelhante a esta:

CREATE TABLE item (
    id INTEGER PRIMARY KEY,
    description TEXT
);

CREATE TABLE item_attr (
    item INTEGER REFERENCES item(id),
    name TEXT,
    value INTEGER,
    PRIMARY KEY (item, name)
);

INSERT INTO item VALUES (1, 'Item 1');
INSERT INTO item_attr VALUES (1, 'height', 20);
INSERT INTO item_attr VALUES (1, 'width', 30);
INSERT INTO item_attr VALUES (1, 'weight', 40);
INSERT INTO item VALUES (2, 'Item 2');
INSERT INTO item_attr VALUES (2, 'height', 10);
INSERT INTO item_attr VALUES (2, 'weight', 35);

(Entendo que o EAV é um pouco controverso, mas esta questão não é sobre o EAV: este aplicativo herdado não pode ser alterado de qualquer maneira.)

Pode haver vários atributos, mas geralmente até 200 atributos por itens (geralmente semelhantes). Desses 200 atributos, há um grupo de cerca de 25 que são mais comuns que os demais e que são usados ​​com mais frequência nas consultas.

Para tornar mais fácil escrever novas consultas com base em alguns desses 25 atributos (os requisitos tendem a mudar e preciso ser flexível), escrevi uma visão que une a tabela de atributos para esses 25 atributos. Seguindo o exemplo acima, fica assim:

CREATE VIEW exp_item AS SELECT
   i.id AS id,
   i.description AS description,
   ia_height.value AS height,
   ia_width.value AS width,
   ia_weight.value AS weight,
   ia_depth.value AS depth
FROM item i
  LEFT JOIN item_attr ia_height ON i.id=ia_height.item AND ia_height.name='height'
  LEFT JOIN item_attr ia_width ON i.id=ia_width.item AND ia_width.name='width'
  LEFT JOIN item_attr ia_weight ON i.id=ia_weight.item AND ia_weight.name='weight'
  LEFT JOIN item_attr ia_depth ON i.id=ia_depth.item AND ia_depth.name='depth';

Um relatório típico usaria apenas alguns desses 25 atributos, por exemplo:

SELECT id, description, height, width FROM exp_item;

Algumas dessas consultas não são tão rápidas quanto eu gostaria que fossem. Com EXPLAINo , notei que ainda eram feitos joins nas colunas não utilizadas, o que, em cerca de 25 joins quando apenas 3 ou 4 atributos são usados, está causando uma degradação desnecessária no desempenho.

É claro que executar todos os LEFT JOINs na exibição é normal, mas estou pensando se haveria uma maneira de manter essa exibição (ou algo semelhante: estou interessado principalmente em usar uma exibição para simplificar a maneira como me refiro aos atributos , mais ou menos como se fossem colunas) e evitar (automaticamente) o uso de joins nos atributos não utilizados para uma determinada consulta.

A única solução que encontrei até agora é definir uma visão específica para cada uma dessas consultas, que só se junta com base nos atributos que são usados. (Isso melhora a velocidade, como esperado, mas requer mais programação de visualizações todas as vezes, portanto, um pouco menos de flexibilidade.)

Existe uma maneira melhor de fazer isso? (Existe uma maneira melhor de "fingir" que a estrutura do EAV é uma única tabela bem estruturada, do ponto de vista de escrever as consultas, e não ter que fazer essas junções à esquerda desnecessárias?)

Estou usando o PostgreSQL 8.4. Existem cerca de 10.000 linhas iteme cerca de 500.000 linhas em item_attr. Eu não esperaria mais de 80 mil linhas iteme 4 milhões de linhas item_attr, o que acredito que um sistema moderno pode manipular sem muitos problemas. (Comentários sobre outros RDBMS/versões também são bem-vindos.)

EDIT : Apenas para expandir o uso de índices neste exemplo.

O PRIMARY KEY (item, name)cria implicitamente um índice em (item, name), conforme documentado na documentação CREATE TABLE . Considerando que ambos iteme namesão usados ​​com uma restrição de igualdade no JOIN, esse índice parece adequado de acordo com a documentação sobre índices multicolunas .

O exemplo a seguir mostra que esse índice parece ser usado, conforme esperado, sem nenhum índice adicional explícito:

EXPLAIN SELECT id, description, height, width FROM exp_item WHERE width < 100;

                                                QUERY PLAN                                                 
-----------------------------------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=28.50..203.28 rows=10 width=20)
   ->  Nested Loop Left Join  (cost=28.50..196.73 rows=10 width=16)
         ->  Nested Loop Left Join  (cost=28.50..190.18 rows=10 width=16)
               ->  Hash Join  (cost=28.50..183.64 rows=10 width=16)
                     Hash Cond: (ia_width.item = i.id)
                     ->  Seq Scan on item_attr ia_width  (cost=0.00..155.00 rows=10 width=8)
                           Filter: ((value < 100) AND (name = 'width'::text))
                     ->  Hash  (cost=16.00..16.00 rows=1000 width=12)
                           ->  Seq Scan on item i  (cost=0.00..16.00 rows=1000 width=12)
               ->  Index Scan using item_attr_pkey on item_attr ia_depth  (cost=0.00..0.64 rows=1 width=4)
                     Index Cond: ((i.id = ia_depth.item) AND (ia_depth.name = 'depth'::text))
         ->  Index Scan using item_attr_pkey on item_attr ia_weight  (cost=0.00..0.64 rows=1 width=4)
               Index Cond: ((i.id = ia_weight.item) AND (ia_weight.name = 'weight'::text))
   ->  Index Scan using item_attr_pkey on item_attr ia_height  (cost=0.00..0.64 rows=1 width=8)
         Index Cond: ((i.id = ia_height.item) AND (ia_height.name = 'height'::text))
postgresql performance
  • 3 3 respostas
  • 3792 Views

3 respostas

  • Voted
  1. Best Answer
    gbn
    2011-06-27T23:37:08+08:002011-06-27T23:37:08+08:00

    Esta é uma (das muitas) desvantagens dos designs de EAV.

    Você realmente não pode melhorar o JOIN: devido à complexidade necessária, um otimizador baseado em custo não chegará ao plano perfeito. Acha "bom o suficiente"

    Sugestões:

    • não use uma exibição: use consultas de tipo agregado (por exemplo, COUNT(*) = 2 se eu corresponder à altura e ao peso)
    • use um gatilho para manter uma tabela real (ou esparsa) e consultar que

    A primeira opção escala melhor porque alguns índices na tabela de fatos principal do EAV podem cobrir todas as consultas bem.

    • 7
  2. Denis de Bernardy
    2011-06-27T22:34:51+08:002011-06-27T22:34:51+08:00

    Você não menciona índices na tabela eav, então estou assumindo que você não tem nenhum.

    Pode fazer sentido adicionar alguns parciais. Dependendo do tipo de consulta que você está fazendo, um ou ambos podem ser úteis:

    create index item_attr_weight_item_idx
      on item_attr(item)
      where (name = 'weight');
    
    create index item_attr_weight_value_idx
      on item_attr(value)
      where (name = 'weight');
    

    Como alternativa, como você tem um pequeno número de linhas, um índice grande (name, value)ou gordo (name, item)pode funcionar. Este último também pode ser parcial, por exemplo:

    create index item_attr_freq_item_idx
      on item_attr(name, item)
      where (name in ('weight', 'height', 'width'));
    

    Dessa forma, pelo menos o planejador de consulta terá algo mais material para trabalhar.

    • 2
  3. Mike Sherrill 'Cat Recall'
    2011-07-01T15:49:56+08:002011-07-01T15:49:56+08:00

    Eu consideraria tentar o módulo hstore do PostgreSQL .

    • 1

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