sql >> Database >  >> RDS >> PostgreSQL

Vraag laatste N gerelateerde rijen per rij op

Uitgaande van ten minste Postgres 9.3.

Index

Ten eerste zal een index met meerdere kolommen helpen:

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

created_at DESC past iets beter, maar de index zou nog steeds met bijna dezelfde snelheid achteruit worden gescand zonder DESC .

Ervan uitgaande dat created_at is gedefinieerd NOT NULL , overweeg anders DESC NULLS LAST in index en vraag:

  • PostgreSQL sorteren op datum/tijd asc, eerst null?

De laatste kolom id is alleen handig als je er een scan met alleen index uit haalt, wat waarschijnlijk niet zal werken als je constant veel nieuwe rijen toevoegt. Verwijder in dit geval id uit de index.

Eenvoudigere zoekopdracht (nog steeds traag)

Vereenvoudig je zoekopdracht, de binnenste subselect helpt niet:

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;

Zou iets sneller moeten zijn, maar nog steeds traag.

Snelle zoekopdracht

  • Ervan uitgaande dat je relatief weinig . hebt zenders en relatief veel waarnemingen per station.
  • Ook uitgaande van station_id id gedefinieerd als NOT NULL .

Om echt te zijn snel is, hebt u het equivalent van een losse indexscan nodig (nog niet geïmplementeerd in Postgres). Gerelateerd antwoord:

  • Optimaliseer de GROUP BY-query om het laatste record per gebruiker op te halen

Als je een aparte tabel hebt met stations (wat waarschijnlijk lijkt), kun je dit emuleren met 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;

Als je geen tabel hebt met stations , zou het beste zijn om er een te maken en te onderhouden. Voeg eventueel een refererende sleutelreferentie toe om relationele integriteit af te dwingen.

Als dat geen optie is, kun je zo'n tabel on-the-fly distilleren. Eenvoudige opties zijn:

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

Maar beide zouden een sequentiële scan nodig hebben en traag zijn. Laat Postgres bovenstaande index gebruiken (of een willekeurige btree-index met station_id als leidende kolom) met een recursieve 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

Gebruik dat als drop-in vervanging voor de stations tabel in de bovenstaande eenvoudige vraag:

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;

Dit zou nog steeds sneller moeten zijn dan wat je had met orden van grootte .

SQL Fiddle hier (9.6)
db<>fiddle hier



  1. Fout bij het instellen van n_distinct met behulp van een plpgsql-variabele

  2. Gegevens invoegen met mysqli

  3. TOON TABELLEN in MariaDB

  4. Genereer SQL om de primaire sleutel bij te werken