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 / 229204
Accepted
A_V
A_V
Asked: 2019-02-08 18:33:28 +0800 CST2019-02-08 18:33:28 +0800 CST 2019-02-08 18:33:28 +0800 CST

Otimização do Postgres CTE com json_build_object aninhado

  • 772

Estou tentando escrever uma consulta que retorna dados de várias tabelas e os agrega em um campo JSON aninhado. Eu sinto que isso teria um ótimo desempenho no SqlServer, mas, como Brent Ozar escreveu neste post, o otimizador do Postgres isola as consultas CTE juntas. Isso me força a usar uma WHEREinstrução no primeiro nível do CTE, pois, caso contrário, carregaria todo o conjunto de dados todas as vezes. Isso e as funções JSON específicas que eu realmente não estou acostumado a me fazer pensar se isso poderia ter um desempenho melhor.

Eu tentei escrever isso sem um CTE, mas não sabia como aninhar subconsultas.

Existe algum truque postgres que estou perdendo aqui? Esses índices são eficazes?

A saída se parece com isso:

[{
    "item_property_id": 1001010,
    "property_name": "aadb480d8716e52da33ed350b00d6cef",
    "values": [
        "1f64450fae03b127cf95f9b06fca4bca",
        "9a6883b8a87a5028bf7dfc27412c2de8"
    ]
},{
    "item_property_id": 501010,
    "property_name": "e870e8d81e16ee46c75493856b4c6b66",
    "values": [
        "a6bed25b407c515bb8a55f2e239066ec",
        "feb10299fd6408e0d37a8761e334c97a"
    ]
},{
    "item_property_id": 1010,
    "property_name": "f2d7b27c50a059d9337c949c13aa3396",
    "values": [
        "56674c1c3d66c832abf87b436a4fd095",
        "ff88fe69f4438a6277c792faaf485368"
    ]
}]

Aqui está o script para gerar o esquema e testar os dados

--create schema
drop table if exists public.items;
drop table if exists public.items_properties;
drop table if exists public.items_properties_values;
create table public.items(
    item_id integer primary key,
    item_name varchar(250));                      
create table public.items_properties(
    item_property_id serial primary key,
    item_id integer,
    property_name varchar(250));                      
create table public.items_properties_values(
    item_property_value_id serial primary key,
    item_property_id integer,
    property_value varchar(250));
CREATE INDEX items_index
    ON public.items USING btree
    (item_id ASC NULLS LAST,item_name asc nulls last)
    TABLESPACE pg_default; 
CREATE INDEX properties_index
    ON public.items_properties USING btree
    (item_property_id ASC NULLS LAST,item_id asc nulls last,property_name asc nulls last)
    TABLESPACE pg_default;
CREATE INDEX values_index
    ON public.items_properties_values USING btree
    (item_property_value_id ASC NULLS LAST,item_property_id asc nulls last,property_value asc nulls last)
    TABLESPACE pg_default;

--insert dummy data
insert into public.items                        
SELECT generate_series(1,500000),md5(random()::text);

insert into public.items_properties (item_id,property_name)
SELECT item_id,md5(random()::text) from public.items;
insert into public.items_properties (item_id,property_name)
SELECT item_id,md5(random()::text) from public.items;
insert into public.items_properties (item_id,property_name)
SELECT item_id,md5(random()::text) from public.items;


insert into public.items_properties_values (item_property_id,property_value)
select item_property_id,md5(random()::text) from public.items_properties;
insert into public.items_properties_values (item_property_id,property_value)
select item_property_id,md5(random()::text) from public.items_properties;

--Query returned successfully in 22 secs 704 msec.

Aqui está o comando SQL

Sem o where na terceira linha, leva ~ 15 segundos para carregar. Eu entendo que isso está carregando milhares de registros, então talvez esteja funcionando bem, mas eu REALMENTE gostaria de uma segunda opinião.

with cte_items as (
    select item_id,item_name from public.items  
    --where item_id between 1000 and 1010
),cte_properties as (
    select ip.item_id,ip.item_property_id,ip.property_name from public.items_properties ip
    inner join cte_items i on i.item_id=ip.item_id
),cte_values as (
    select ipv.item_property_value_id,ipv.item_property_id,ipv.property_value from public.items_properties_values ipv
    inner join cte_properties p on ipv.item_property_id=p.item_property_id
)
select i.item_id,i.item_name,json_agg(json_build_object('item_property_id',prop.item_property_id,'property_name',prop.property_name,'values',prop.values))
from cte_items i
left join (
    select cp.item_id,cp.item_property_id,cp.property_name,json_agg(to_json(cv.property_value)) "values"
    from cte_properties cp
    left join ( select val.item_property_id,val.property_value from cte_values val ) cv on cv.item_property_id=cp.item_property_id
    group by cp.item_id,cp.item_property_id,cp.property_name
) prop
on i.item_id=prop.item_id
group by i.item_id,i.item_name
postgresql postgresql-9.6
  • 2 2 respostas
  • 2479 Views

2 respostas

  • Voted
  1. jjanes
    2019-02-10T07:05:07+08:002019-02-10T07:05:07+08:00

    Você (ou Brent) está certo de que CTEs são cercas de otimização no PostgreSQL. Há um trabalho ativo para remover essa limitação, mas não estou muito otimista de que esse trabalho seja incorporado na próxima versão, v12 .

    Raramente uso CTEs somente de seleção no código de produção. Se o CTE for apenas de seleção e não contiver nenhum parâmetro substituível, geralmente apenas crio uma exibição a partir dele. O que eu acho que é um código melhor, além de fugir do problema da cerca de otimização. De fato, os únicos lugares em que se pode encontrar alguns CTEs somente de seleção no meu código de produção são onde eu preciso especificamente do comportamento da cerca de otimização, para evitar que o planejador otimize incorretamente as consultas com base em correlações que eu conheço, mas o planejador não .

    • 1
  2. Best Answer
    Erwin Brandstetter
    2019-02-10T16:46:55+08:002019-02-10T16:46:55+08:00

    O que @jjanes escreveu sobre CTEs atuando como cerca de otimização.

    Sua consulta específica não precisa de CTEs para começar - nem a maioria dos outros ruídos incluídos. O que vejo pode ser reduzido a um SELECTcom dois níveis de subconsultas aninhadas:

    SELECT item_id, item_name, js
    FROM   items i
    LEFT   JOIN (
       SELECT item_id, json_agg(json_build_object('item_property_id',item_property_id,'property_name',property_name,'values',values)) AS js
       FROM   items_properties
       LEFT   JOIN (
          SELECT item_property_id, json_agg(property_value) AS values
          FROM   items_properties_values
          GROUP  BY 1
          ) ipv USING (item_property_id)
       GROUP  BY 1
       ) ip USING (item_id)
    ORDER  BY 1, 2;
    

    db<>fique aqui

    Foi duas vezes mais rápido no meu teste rápido.

    Ao consultar tabelas inteiras, também é muito mais rápido agregar primeiro e unir depois . Ainda mais quando você tem mais do que apenas 2 ou 3 linhas por agregado, como em sua demonstração - o que pode ser simplificado demais.

    Relacionado:

    • Várias chamadas array_agg() em uma única consulta
    • Consultas lentas relacionadas a subconsultas usando agregação
    • 1

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • 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