PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Otimize a consulta máxima de grupo


Supondo que relativamente poucos linhas em options para muitas linhas em records .

Normalmente, você teria uma pesquisa de tabela options que é referenciado em records.option_id , de preferência com uma restrição de chave estrangeira. Se você não fizer isso, sugiro criar um para impor a integridade referencial:
CREATE TABLE options (
  option_id int  PRIMARY KEY
, option    text UNIQUE NOT NULL
);

INSERT INTO options
SELECT DISTINCT option_id, 'option' || option_id -- dummy option names
FROM   records;

Então não há mais necessidade de emular uma varredura de índice solta e isso se torna muito simples e rápido . Subconsultas correlacionadas podem usar um índice simples em (option_id, id) .
SELECT option_id, (SELECT max(id)
                   FROM   records
                   WHERE  option_id = o.option_id) AS max_id
FROM   options o
ORDER  BY 1;

Isso inclui opções sem correspondência na tabela records . Você recebe NULL para max_id e você pode remover facilmente essas linhas em um SELECT externo se necessário.

Ou (mesmo resultado):
SELECT option_id, (SELECT id
                   FROM   records
                   WHERE  option_id = o.option_id
                   ORDER  BY id DESC NULLS LAST
                   LIMIT  1) AS max_id
FROM   options o
ORDER  BY 1;

Pode ser um pouco mais rápido. A subconsulta usa a ordem de classificação DESC NULLS LAST - igual à função agregada max() que ignora valores NULL. Classificando apenas DESC teria NULL primeiro:
  • Por que os valores NULL vêm primeiro ao ordenar DESC em uma consulta do PostgreSQL?

O índice perfeito para isso:
CREATE INDEX on records (option_id, id DESC NULLS LAST);

A ordem de classificação do índice não importa muito enquanto as colunas são definidas NOT NULL .

Ainda pode haver uma varredura sequencial na pequena tabela options , essa é apenas a maneira mais rápida de buscar todas as linhas. O ORDER BY pode trazer uma varredura de índice (somente) para buscar linhas pré-ordenadas.
A grande tabela records só é acessado via varredura de índice (bitmap) ou, se possível, varredura somente de índice .

db<>mexa aqui - mostrando duas varreduras somente de índice para o caso simples
Old sqlfiddle

Ou use LATERAL joins para um efeito semelhante no Postgres 9.3+:
  • Otimize a consulta GROUP BY para recuperar a última linha por usuário