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

Zoptymalizuj maksymalne zapytanie grupowe

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. usuń kolumnę nie istnieje

  2. Różnica sygnatury czasowej w godzinach dla PostgreSQL

  3. Nie znaleziono obrazu Psycopg2

  4. PostgreSQL:Utwórz tabelę, jeśli nie istnieje AS

  5. HikariCP - połączenie nie jest dostępne