Zakładając, że stosunkowo niewiele wiersze w options dla wielu wierszy w records .
Zazwyczaj będziesz mieć wyszukiwarkę tabeli options do którego odwołuje się records.option_id , najlepiej z ograniczeniem klucza obcego. Jeśli nie, sugeruję utworzenie takiego, aby wymusić integralność referencyjną:
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;
Wtedy nie ma już potrzeby emulowania luźnego skanowania indeksu, a to staje się bardzo proste i szybkie . Skorelowane podzapytania mogą używać zwykłego indeksu na (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;
Obejmuje to opcje bez dopasowania w tabeli records . Otrzymujesz NULL dla max_id i możesz łatwo usunąć takie wiersze w zewnętrznym SELECT w razie potrzeby.
Lub (ten sam wynik):
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;
Może być nieco szybszy. Podzapytanie używa kolejności sortowania DESC NULLS LAST - tak samo jak funkcja agregująca max() który ignoruje wartości NULL. Sortowanie tylko DESC najpierw miałby NULL:
- Dlaczego wartości NULL są na pierwszym miejscu przy zamawianiu DESC w zapytaniu PostgreSQL?
Idealny indeks do tego:
CREATE INDEX on records (option_id, id DESC NULLS LAST);
Kolejność sortowania indeksu nie ma większego znaczenia, gdy kolumny są zdefiniowane NOT NULL .
Nadal może istnieć sekwencyjne skanowanie małej tabeli options , to po prostu najszybszy sposób na pobranie wszystkich wierszy. ORDER BY może wprowadzić skanowanie indeksu (tylko) w celu pobrania wstępnie posortowanych wierszy.
Duża tabela records jest dostępny tylko poprzez skanowanie indeksu (map bitowych) lub, jeśli to możliwe, skanowanie tylko z indeksem .
db<>graj tutaj - pokazywanie dwóch skanów samego indeksu dla prostego przypadku
Stary sqlfiddle
Lub użyj LATERAL łączy dla podobnego efektu w Postgresie 9.3+:
- Zoptymalizuj zapytanie GROUP BY, aby pobrać ostatni wiersz na użytkownika