sql >> Databáze >  >> RDS >> PostgreSQL

Optimalizujte skupinový maximální dotaz

Za předpokladu relativně málo řádků v options pro mnoho řádků v records .

Obvykle byste měli vyhledávací tabulku options na který se odkazuje z records.option_id , ideálně s omezením cizího klíče. Pokud tak neučiníte, navrhuji vytvořit jeden pro vynucení referenční integrity:

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;

Pak již není potřeba emulovat volné prohledávání indexů a toto se stává velmi jednoduchým a rychlým . Korelované poddotazy mohou používat prostý index na (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;

To zahrnuje možnosti, které v tabulce records neodpovídají . Pro max_id získáte hodnotu NULL a takové řádky můžete snadno odstranit ve vnějším SELECT v případě potřeby.

Nebo (stejný výsledek):

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;

Může být o něco rychlejší. Poddotaz používá pořadí řazení DESC NULLS LAST - stejné jako agregační funkce max() který ignoruje hodnoty NULL. Řazení pouze DESC bude mít nejprve NULL:

  • Proč jsou hodnoty NULL na prvním místě při objednávání DESC v dotazu PostgreSQL?

Perfektní index pro toto:

CREATE INDEX on records (option_id, id DESC NULLS LAST);

Na pořadí řazení indexu příliš nezáleží, pokud jsou sloupce definovány NOT NULL .

Stále může být sekvenční skenování na malém stole options , to je jen nejrychlejší způsob, jak načíst všechny řádky. ORDER BY může přinést (pouze) skenování indexu pro načtení předem seřazených řádků.
Velká tabulka records je přístupný pouze prostřednictvím (bitmapového) indexového skenování nebo, je-li to možné, pouze indexového skenování .

db<>zde hrajte - zobrazující dvě skenování pouze s indexem pro jednoduchý případ
Starý sqlfiddle

Nebo použijte LATERAL se připojí pro podobný efekt v Postgres 9.3+:

  • Optimalizujte dotaz GROUP BY pro načtení posledního řádku na uživatele


  1. Sloupce MySQL s DEFAULT NULL - stylistická volba, nebo ano?

  2. Je nutné DbCommand po použití zlikvidovat?

  3. SQLite levé připojení

  4. Jak importovat a exportovat databázi přes phpMyAdmin (chyba 'Přístup odepřen vytvořit databázi db_name')