sql >> Database teknologi >  >> RDS >> PostgreSQL

Forespørg på de sidste N relaterede rækker pr. række

Forudsat mindst Postgres 9.3.

Indeks

For det første vil et indeks med flere kolonner hjælpe:

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

created_at DESC passer lidt bedre, men indekset ville stadig blive scannet baglæns med næsten samme hastighed uden DESC .

Forudsat created_at er defineret NOT NULL , ellers overveje DESC NULLS LAST i indeks og forespørgsel:

  • PostgreSQL sorteres efter datetime asc, null først?

Den sidste kolonne id er kun nyttig, hvis du får en kun indeksscanning ud af det, hvilket sandsynligvis ikke vil fungere, hvis du tilføjer masser af nye rækker konstant. I dette tilfælde skal du fjerne id fra indekset.

Enklere forespørgsel (stadig langsom)

Forenkle din forespørgsel, det indre undervalg hjælper ikke:

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;

Bør være en smule hurtigere, men stadig langsomt.

Hurtig forespørgsel

  • Forudsat at du har relativt stationer og relativt mange observationer pr. station.
  • Under forudsætning af station_id id defineret som NOT NULL .

At være virkelig hurtigt, du har brug for, hvad der svarer til en løs indeksscanning (endnu ikke implementeret i Postgres). Relateret svar:

  • Optimer GROUP BY-forespørgsel for at hente seneste post pr. bruger

Hvis du har en separat tabel over stations (hvilket virker sandsynligt), du kan efterligne dette med 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;

Hvis du ikke har en tabel over stations , ville det næstbedste være at oprette og vedligeholde en. Tilføj eventuelt en fremmednøglereference for at håndhæve relationel integritet.

Hvis det ikke er en mulighed, kan du destillere sådan et bord i farten. Enkle muligheder ville være:

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

Men begge ville have brug for en sekventiel scanning og være langsom. Få Postgres til at bruge ovenstående indeks (eller ethvert btree-indeks med station_id som ledende kolonne) med en rekursiv 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

Brug det som drop-in-erstatning for stations tabel i ovenstående simple forespørgsel:

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;

Dette burde stadig være hurtigere end hvad du havde i størrelsesordener .

SQL Fiddle her (9.6)
db<>fiddle her



  1. Sådan får du første række pr. gruppe i PostgreSQL

  2. Hierarkisk SQL-spørgsmål

  3. Tilslutning af IRI-software til Oracle

  4. Sådan sikkerhedskopieres MySQL-databaser ved hjælp af AutoMySQLBackup