PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Zapytaj o ostatnie N powiązanych wierszy na wiersz

Zakładając co najmniej Postgres 9.3.

Indeks

Po pierwsze, wielokolumnowy indeks pomoże:

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

created_at DESC jest nieco lepiej dopasowany, ale indeks nadal byłby skanowany wstecz z prawie taką samą prędkością bez DESC .

Zakładając, że created_at jest zdefiniowany NOT NULL , w przeciwnym razie rozważ DESC NULLS LAST w indeksie i zapytanie:

  • PostgreSQL sortuje według daty i godziny asc, najpierw null?

Ostatnia kolumna id przydaje się tylko wtedy, gdy uzyskujesz z niego tylko skan indeksu, który prawdopodobnie nie zadziała, jeśli stale dodasz wiele nowych wierszy. W takim przypadku usuń id z indeksu.

Prostsze zapytanie (nadal wolne)

Uprość zapytanie, wewnętrzny podselekcja nie pomoż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;

Powinien być nieco szybszy, ale nadal wolny.

Szybkie zapytanie

  • Zakładając, że masz stosunkowo niewiele stacje i stosunkowo wiele obserwacje na stację.
  • Również zakładając station_id id zdefiniowany jako NOT NULL .

Być naprawdę szybko, potrzebujesz odpowiednika luźnego skanowania indeksu (jeszcze nie zaimplementowane w Postgresie). Powiązana odpowiedź:

  • Zoptymalizuj zapytanie GROUP BY, aby pobrać najnowszy rekord na użytkownika

Jeśli masz oddzielną tabelę stations (co wydaje się prawdopodobne), możesz to emulować za 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;

Jeśli nie masz tabeli stations , następną najlepszą rzeczą byłoby utworzenie i utrzymanie takiego. Ewentualnie dodaj odwołanie do klucza obcego, aby wymusić integralność relacyjną.

Jeśli to nie jest opcja, możesz destylować taki stół w locie. Proste opcje to:

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

Ale albo potrzebowałby sekwencyjnego skanowania i byłby powolny. Niech Postgres używa powyższego indeksu (lub dowolnego indeksu btree z station_id jako wiodąca kolumna) z rekurencyjnym 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

Użyj tego jako zamiennika drop-in dla stations tabela w powyższym prostym zapytaniu:

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;

To nadal powinno być szybsze niż to, co miałeś o rzędy wielkości .

SQL Fiddle tutaj (9.6)
db<>Tutaj grać



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak wykonać operacje aktualizacji na kolumnach typu JSONB w Postgresie 9.4?

  2. Aktualizacja JLabel przez SetIcon z typu danych bytea w postgres

  3. PostgreSQL użyj wartości z poprzedniego wiersza, jeśli jej brakuje

  4. Nie można wybrać z klauzuli UPDATE RETURNING w postgres

  5. Wyszukaj w tablicy JSON obiekt zawierający wartość pasującą do wzorca