1

Temat: Join i wolne sortowanie

Witam,
Dla uproszczenia mam dwie tabele (offers - oferty czyli opiso wycieczek/hoteli, termins - lista terminów do oferty):

CREATE TABLE "offers"(
    "offerId" SERIAL PRIMARY KEY,
    "name" varchar(128) NOT NULL,
    ...);

CREATE TABLE "termins"(
    "terminId" SERIAL PRIMARY KEY,
    "offerId" int NOT NULL REFERENCES "offers" ON DELETE CASCADE ON UPDATE CASCADE,
    "dateFrom" date NOT NULL,
    "price" numeric NOT NULL,
    ...);

Wykonujemy następujące zapytanie, którego celem jest wyszukanie ofert z najlepszym terminem spełniającym warunki.

SELECT
    *
FROM
    (
    SELECT
        DISTINCT ON("offerId") te."terminId", te."dateFrom", te."price", te."offerId"
    FROM
        termins te
    WHERE
        "dateFrom" > now()
    ORDER BY
        "offerId", "price"
    ) t
INNER JOIN
    "offers" o ON(o."offerId" = t."offerId")
LIMIT 10
Limit  (cost=0.00..42.10 rows=10 width=187)

  ->  Merge Left Join  (cost=0.00..10348.97 rows=2458 width=187)

        Merge Cond: (te."offerId" = o."offerId")

        ->  Unique  (cost=0.00..9002.22 rows=2458 width=44)

              ->  Index Scan using "offerId_price_idx" on termins te  (cost=0.00..8722.18 rows=112013 width=44)

                    Filter: ("dateFrom" > now())

        ->  Index Scan using offers_pkey on offers o  (cost=0.00..1274.61 rows=6735 width=143)


Teraz dodajemy sortowanie po kolumnie "price":

SELECT
    *
FROM
    (
    SELECT
        DISTINCT ON("offerId") te."terminId", te."dateFrom", te."price", te."offerId"
    FROM
        termins te
    WHERE
        "dateFrom" > now()
    ORDER BY
        "offerId", "price"
    ) t
INNER JOIN
    "offers" o ON(o."offerId" = t."offerId")
ORDER BY
    "price"
LIMIT 10
Limit  (cost=9863.39..9863.42 rows=10 width=187)

  ->  Sort  (cost=9863.39..9869.54 rows=2458 width=187)

        Sort Key: te.price

        ->  Hash Left Join  (cost=562.54..9810.27 rows=2458 width=187)

              Hash Cond: (te."offerId" = o."offerId")

              ->  Unique  (cost=0.00..9002.21 rows=2458 width=44)

                    ->  Index Scan using "offerId_price_idx" on termins te  (cost=0.00..8722.18 rows=112012 width=44)

                          Filter: ("dateFrom" > now())

              ->  Hash  (cost=339.35..339.35 rows=6735 width=143)

                    ->  Seq Scan on offers o  (cost=0.00..339.35 rows=6735 width=143)

Czas wykonania zapytania zwiększa się około 10 na lokalnym komputerze (110 000 terminów i 6700 ofert).

Czas wykonania zapytania na serwerze do docelowym zwiększa się około 200 razy (310 000 terminów - docelowo 1 000 000, 12 000 ofert docelowo - 20 000).

Zapytanie na serwerze docelowym trwa już około 1-1.5 sekundy, a do tego jeszcze dojdą left joiny do 5-6 tabel zawierających nazwę miasta, kraju, typ zakwaterowania.. ale to nie ma tutaj znaczenia.

Czy jest szansa, aby to zapytanie trwało krócej? Zastanawiam się czy jakieś indeksy można tutaj potworzyć np. by hash left join przyspieszyć?

Mam założony indeks na "price" oraz wspólny na "offerId" i "price". Nie mam założonego indeksu na klucz obcy "offerId".

2

Odp: Join i wolne sortowanie

Czy to zapytanie musi tak wyglądać? Po co są te dwa sortowania. Czemu używasz zapytań zagnieżdżonych, nie próbowałeś tego zrobić zwykłym zapytaniem (select po dwóch tabelach)?

3

Odp: Join i wolne sortowanie

Nie wiem czy korzystałeś kiedyś z wyszukiwarki biura podróży, ale wygląda to mniej więcej tak :

jest lista ofert, a każda oferta posiada pewną ilość terminów, czyli mamy np. :

oferta 1) Hotel Janosik
oferta 2) Apartament Hulo

oferta 1) posiada następujące terminy:
termin (1) od 20 czerwca do 27 czerwca, cena 800 zł
termin (2) od 27 czerwca do 10 lipca, cena 900 zł
termin (3) od 10 lipca do 17 lipca, cena 700 zł


oferta 2) posiada następujące terminy:
termin (4) od 20 czerwca do 27 czerwca, cena 500 zł
termin (5) od 27 czerwca do 10 lipca, cena 700 zł
termin (6) od 10 lipca do 17 lipca, cena 600 zł

teraz uruchamiasz wyszukiwarkę (nie wybierasz żadnych kryteriów, domyślnie sortowanie jest po cenie) i otrzymujesz następujący wynik:

oferta 2) termin (4)
oferta 1) termin (3)

czyli wyszukujesz w terminach najtańsze terminy (distinct + sortowanie po cenie), a następnie sortujesz już wszystkie oferty także po cenie by najniższa była najwyżej

podobny mechanizm można zaobserwować w ceneo.pl niestety tutaj sprawa jest bardziej skomplikowana ponieważ użytkownik może wybrać zakres czasowy co uniemożliwia zapisanie w tabli oferta np. najtańszej oferty z terminów

Ostatnio edytowany przez stormfly (2009-06-16 20:23:19)

4

Odp: Join i wolne sortowanie

To może zacznij od indeksu na offerId. Robiłeś vaccum i analyze?

5

Odp: Join i wolne sortowanie

Mała literówka - vacuum. Spodziewałem się raczej jakieś konkretniejszej odpowiedzi po spojrzeniu na zapytanie i explain. Zwłaszcza, że nie jest ono jakieś super zakręcone. Proste podzapytanie z distinct + złączenie.

Nie wiem jak to jest z osobami zajmującymi się na co dzień bazami, ale w programowaniu od razu po spojrzeniu na kod + opis co mu nie pasuje człowiek jest w stanie podać dość precyzyjne punkt zaczepienia do dalszej pracy. Próbuje dzisiaj od 8 rano różnych ustawień z indeksami i analizowaniem wyników, ale to jest na zasadzie chybił trafił bo nie zajmuje się takimi optymalizacjami na co dzień. Więc nie chodzi mi o pomoc jak dalej "strzelać" wink Prosiłbym o jakieś konkrety.

6

Odp: Join i wolne sortowanie

No jest konkret index na offerId. Poza tym muszę cię trochę zmartwić ale w przeciwieństwie do programowania, w systemach bazach danych nie ma gotowych rozwiązań ('wzorców projektowych'). Zapytanie może różnie się działać w zależności od rozkładu danych w tabelach i nie ma złotego środka. I optymalizacja zazwyczaj polega na jak to nazywasz 'strzelaniu'. To że twoje zapytanie zacznie działać dla 310000 wierszy nie znaczy ze sie nie posypie przy ilości  1000000.

Pewnie warto tez pomyśleć o partycjonowaniu tabel ale niestety to trzeba jeśli masz mieć w przyszłości 1000000 wierszy.

Ostatnio edytowany przez rski (2009-06-16 21:33:45)