sql >> Databasteknik >  >> RDS >> PostgreSQL

Optimera gruppvis maximal fråga

Förutsatt att det är relativt rader i options för många rader i records .

Vanligtvis skulle du ha en uppslagstabell options som refereras från records.option_id , helst med en främmande nyckel-begränsning. Om du inte gör det, föreslår jag att du skapar en för att upprätthålla referensintegritet:

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;

Då finns det ingen anledning att emulera en lös indexskanning längre och detta blir mycket enkelt och snabbt . Korrelerade underfrågor kan använda ett vanligt index på (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;

Detta inkluderar alternativ utan matchning i tabellen records . Du får NULL för max_id och du kan enkelt ta bort sådana rader i en yttre SELECT om det behövs.

Eller (samma resultat):

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;

Kan vara något snabbare. Underfrågan använder sorteringsordningen DESC NULLS LAST - samma som aggregatfunktionen max() som ignorerar NULL-värden. Sorterar bara DESC skulle ha NULL först:

  • Varför kommer NULL-värden först när man beställer DESC i en PostgreSQL-fråga?

Det perfekta indexet för detta:

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

Indexsorteringsordningen spelar inte så stor roll när kolumner är definierade NOT NULL .

Det kan fortfarande göras en sekventiell genomsökning av den lilla tabellen options , det är bara det snabbaste sättet att hämta alla rader. ORDER BY kan ta in en (endast) indexskanning för att hämta försorterade rader.
Den stora tabellen records nås endast via (bitmapps) indexskanning eller, om möjligt, endast indexskanning .

db<>spela här - visar två indexsökningar för det enkla fallet
Old sqlfiddle

Eller använd LATERAL går med för en liknande effekt i Postgres 9.3+:

  • Optimera GROUP BY-frågan för att hämta den senaste raden per användare


  1. Prestanda av SUBSTR på CLOB

  2. MySQL WHERE IN ()

  3. Förstå dödlägen i MySQL och PostgreSQL

  4. Hur man tömmer en MySQL-databas