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