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

Dotaz na posledních N souvisejících řádků na řádek

Za předpokladu alespoň Postgres 9.3.

Index

Nejprve pomůže vícesloupcový index:

CREATE INDEX observations_special_idx
ON observations(station_id, created_at DESC, id)

created_at DESC je o něco lepší, ale index by byl stále skenován pozpátku téměř stejnou rychlostí bez DESC .

Za předpokladu created_at je definováno NOT NULL , jinak zvažte DESC NULLS LAST v indexu a dotaz:

  • PostgreSQL řazení podle datetime asc, nejprve null?

Poslední sloupec id je užitečné pouze tehdy, pokud z něj získáte skenování pouze pro index, což pravděpodobně nebude fungovat, pokud budete neustále přidávat spoustu nových řádků. V tomto případě odeberte id z indexu.

Jednodušší dotaz (stále pomalý)

Zjednodušte svůj dotaz, vnitřní podvýběr nepomůže:

SELECT id
FROM  (
  SELECT station_id, id, created_at
       , row_number() OVER (PARTITION BY station_id
                            ORDER BY created_at DESC) AS rn
  FROM   observations
  ) s
WHERE  rn <= #{n}  -- your limit here
ORDER  BY station_id, created_at DESC;

Mělo by být o něco rychlejší, ale stále pomalé.

Rychlý dotaz

  • Za předpokladu, že jich máte relativně málo stanice a relativně mnoho pozorování na stanici.
  • Také za předpokladu station_id id definováno jako NOT NULL .

Být skutečně rychle, potřebujete ekvivalent volného prohledávání indexů (zatím neimplementováno v Postgresu). Související odpověď:

  • Optimalizujte dotaz GROUP BY pro získání nejnovějšího záznamu na uživatele

Pokud máte samostatnou tabulku stations (což se zdá pravděpodobné), můžete to napodobit pomocí JOIN LATERAL (Postgres 9.3+):

SELECT o.id
FROM   stations s
CROSS  JOIN LATERAL (
   SELECT o.id
   FROM   observations o
   WHERE  o.station_id = s.station_id  -- lateral reference
   ORDER  BY o.created_at DESC
   LIMIT  #{n}  -- your limit here
   ) o
ORDER  BY s.station_id, o.created_at DESC;

Pokud nemáte tabulku stations , další nejlepší věcí by bylo vytvořit a udržovat jeden. Případně přidejte odkaz na cizí klíč k vynucení relační integrity.

Pokud to není možné, můžete takový stůl destilovat za chodu. Jednoduché možnosti by byly:

SELECT DISTINCT station_id FROM observations;
SELECT station_id FROM observations GROUP BY 1;

Ale obojí by potřebovalo sekvenční skenování a bylo by pomalé. Nastavte Postgres, aby používal výše uvedený index (nebo jakýkoli index btree s station_id jako úvodní sloupec) s rekurzivním CTE :

WITH RECURSIVE stations AS (
   (                  -- extra pair of parentheses ...
   SELECT station_id
   FROM   observations
   ORDER  BY station_id
   LIMIT  1
   )                  -- ... is required!
   UNION ALL
   SELECT (SELECT o.station_id
           FROM   observations o
           WHERE  o.station_id > s.station_id
           ORDER  BY o.station_id
           LIMIT  1)
   FROM   stations s
   WHERE  s.station_id IS NOT NULL  -- serves as break condition
   )
SELECT station_id
FROM   stations
WHERE  station_id IS NOT NULL;      -- remove dangling row with NULL

Použijte jej jako náhradu za vložení pro stations tabulka ve výše uvedeném jednoduchém dotazu:

WITH RECURSIVE stations AS (
   (
   SELECT station_id
   FROM   observations
   ORDER  BY station_id
   LIMIT  1
   )
   UNION ALL
   SELECT (SELECT o.station_id
           FROM   observations o
           WHERE  o.station_id > s.station_id
           ORDER  BY o.station_id
           LIMIT  1)
   FROM   stations s
   WHERE  s.station_id IS NOT NULL
   )
SELECT o.id
FROM   stations s
CROSS  JOIN LATERAL (
   SELECT o.id, o.created_at
   FROM   observations o
   WHERE  o.station_id = s.station_id
   ORDER  BY o.created_at DESC
   LIMIT  #{n}  -- your limit here
   ) o
WHERE  s.station_id IS NOT NULL
ORDER  BY s.station_id, o.created_at DESC;

Stále by to mělo být rychlejší než to, co jste měli o řády .

SQL Fiddle zde (9.6)
db<>Fiddle zde



  1. Jak bezpečné je vlákno EnableWriteAheadLogging v kontextu skutečného použití a dokumentace SQLite?

  2. Příkaz SQL WHERE

  3. Funkce SINH() v Oracle

  4. Nejlepší databázové blogy ke sledování