Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Proste zapytanie SQL a długo trwa.
Forum PHP.pl > Forum > Bazy danych > MySQL
pogdan
Dlaczego poniższe zapytanie idzie w minuty tzn trwa do kilku minut w tabeli tr_wydanie jest 800 recordów.
Jest index na id, i na kontrahent_id wg mnie i bez tych indexów powinno chodzić szybko.

  1. SELECT w.id AS w_id
  2. FROM tr_wydanie w WHERE w.id IN (
  3. SELECT max(w2.id)
  4. FROM tr_wydanie w2
  5. WHERE w2.wydanie>0 AND w2.id IN (
  6. SELECT max(id)
  7. FROM tr_wydanie
  8. WHERE wydanie > 0 GROUP BY kontrahent_id
  9. )
  10. GROUP BY w2.kontrahent_id
  11. )


tabela wygląda tak
  1. | id | int(11)
  2. | kontrahent_id | int(11)
  3. | name | varchar(255)
  4. | text | text
  5. | created_on | timestamp
  6. | wplata | double
  7. | action | varchar(64)
  8. | kiedy | datetime
  9.  



Po początkowym wgraniu dumpa chodzi te zapytanie bardzo szybko. Po jakimś czasie trwa kilka minut i już tak zawsze jest. Silnik MyISAM.
sowiq
IMO trochę przekombinowane to zapytanie. Ja bym raczej zrobił 3 oddzielne, bo MySQL lubi się zaplątać przy zagnieżdżonych podzapytaniach.

Co wypluwa Explain?

  1. EXPLAIN
  2. (tutaj całe Twoje zapytanie)



[edit]
No i pierwsze co to wypadałoby zrobić index:
  1. ALTER TABLE tr_wydanie ADD INDEX(id, kontrahent_id)


[edit]
Poprawiłem KEY --> INDEX
pogdan
  1. +----+--------------------+------------+-------+---------------+-----------------------+---------+------+------+-----------------------------+
  2. | id | select_type | TABLE | type | possible_keys | KEY | key_len | ref | rows | Extra |
  3. +----+--------------------+------------+-------+---------------+-----------------------+---------+------+------+-----------------------------+
  4. | 1 | PRIMARY | w | INDEX | NULL | PRIMARY | 4 | NULL | 804 | USING WHERE; USING INDEX |
  5. | 2 | DEPENDENT SUBQUERY | w2 | INDEX | NULL | wydanie_kontrahent_id | 4 | NULL | 9 | USING WHERE; USING filesort |
  6. | 3 | DEPENDENT SUBQUERY | tr_wydanie | INDEX | NULL | wydanie_kontrahent_id | 4 | NULL | 9 | USING WHERE; USING filesort |
  7. +----+--------------------+------------+-------+---------------+-----------------------+---------+------+------+-----------------------------+
  8. 3 rows IN SET (0.00 sec)


ten index to dodatkowo? czy index wydanie_kontrahent_id usunąć
sowiq
Spróbuj dodać indeks na pola id, id_kontrahenta i zobacz czy będzie poprawa. Jeśli nie, to tak jak pisałem - lepiej chyba rozbić to na 3 oddzielne zapytania.
b4rt3kk
Możesz mi wyjaśnić jaki sens ma to podzapytanie?

  1. SELECT max(w2.id)
  2. FROM tr_wydanie w2
  3. WHERE w2.wydanie>0 AND w2.id IN (
  4. SELECT max(id)
  5. FROM tr_wydanie
  6. WHERE wydanie > 0 GROUP BY kontrahent_id
pogdan
tak naprawdę to potrzebuję czegoś takiego gdzie 'kiedy' jest polem typu 'datetime' i sensu nabier dopiero jak jest coś wiećej niż id czyli np. jeszcze name możę teraz już nie bezie take masło maślane.
  1. SELECT w.id AS w_id , w.name AS w_name
  2. FROM tr_wydanie w WHERE w.id IN (
  3. SELECT max(w2.id)
  4. FROM tr_wydanie w2
  5. WHERE w2.wydanie>0 AND w2.kiedy IN (
  6. SELECT max(kiedy)
  7. FROM tr_wydanie
  8. WHERE wydanie > 0 GROUP BY kontrahent_id
  9. )
  10. GROUP BY w2.kontrahent_id
  11. )

i tak naprawdę jest to podzapytanie to zapytania już własciwego ale już tu długo trwa.
natomiast samo
  1. SELECT max(w2.id)
  2. FROM tr_wydanie w2
  3. WHERE w2.wydanie>0 AND w2.kiedy IN (
  4. SELECT max(kiedy)
  5. FROM tr_wydanie
  6. WHERE wydanie > 0 GROUP BY kontrahent_id
  7. )
  8. GROUP BY w2.kontrahent_id

trwa dość szybko. poniżej secundy a nawet 0.00sec pokazuje.
Zupełnie nie rozumiem ma już id wybrane to co ma za problem żeby wybrać po indexie primary_key rekordy (już te z name )



Ja na mój gust to się baza danych sypie. i pewnie sowiqa pomoc zastosuję.
b4rt3kk
Spytam jeszcze o jedno, co chcesz osiągnąć, tj. jakie rekordy pobrać? Bo że zapytanie jest nieoptymalne to wiadomo i od jego optymalizacji bym właśnie zaczął. Powiedz co chcesz pobrać, a spróbuję pomóc.

Cytat(sowiq @ 8.10.2013, 16:38:11 ) *
No i pierwsze co to wypadałoby zrobić index:
  1. ALTER TABLE tr_wydanie ADD KEY(id, kontrahent_id)


Dodanie klucza nie wpłynie na szybkość wyszukiwania... Tutaj należałoby zastosować INDEX.

  1. CREATE INDEX kontrahent_id ON tr_wydanie;

pogdan
sprawa jest rzeczewiscie dziwna bo te zapytanie , robi to samo , i już zasuwa szybko (być może ustanie ta szybkość po jakimś czasie tak przeczuwam)

  1. SELECT w.id AS w_id
  2. FROM tr_wydanie w
  3. INNER JOIN ( SELECT max(w2.id) AS max_id
  4. FROM tr_wydanie w2
  5. WHERE w2.wydanie>0 AND w2.kiedy IN (
  6. SELECT max(kiedy)
  7. FROM tr_wydanie
  8. WHERE wydanie > 0 GROUP BY kontrahent_id
  9. )
  10. GROUP BY w2.kontrahent_id
  11. ) AS w3 ON w.id= w3.max_id



Strange mysql rules
sowiq
Cytat(b4rt3kk @ 8.10.2013, 17:46:02 ) *
Dodanie klucza nie wpłynie na szybkość wyszukiwania... Tutaj należałoby zastosować INDEX.

Oczywiście masz rację. Już poprawiłem w kodzie powyżej. Dzięki za zwrócenie uwagi.
pogdan
Dzięki sowiq i b4rt3kk na razie mi to chodzi jako join a nie jako podzapytanie.

Czym się różni index (id, kontrahent_id) od dwóch indexów odzielnie id i kontrahent_id .
pmir13
Dla mysql nie ma znaczenia które z poniższych składni zastosujemy:
  1. ALTER TABLE t ADD INDEX c(c);
  2. ALTER TABLE t ADD KEY c(c);
  3. CREATE INDEX c ON t(c);

To wszystko dla normalnego indeksu da nam dokładnie ten sam rezultat. Istnieją drobne różnice, ale w naszym przypadku nie mają znaczenia. Jeśli ktoś nie wierzy, proponuję tworzenie i kasowanie indeksu każdą z tych trzech metod i sprawdzanie SHOW INDEXES FROM t;

Indeks na kilku kolumnach, czyli na przykład (kontrahent_id, kiedy) ma w sobie zawartość wszystkich tych kolumn, posortowaną najpierw według kontrahent_id a później dla takich samych kontrahentów według kiedy.
Normalne indeksy, niezależnie od ilości kolumn ( pomijamy fulltext, spatial itp) są typu BTREE, chodzi o to, by przyspieszyć wyszukiwanie rekordów na podobnej zasadzie jak wyszukiwanie binarne przyspiesza na posortowanym zbiorze.
Indeksy wielokolumnowe zawierają w sobie funkcjonalność indeksów dla kolumn z lewej strony, czyli mając taki podwójny nie ma sensu tworzyć następnego indeksu na pojedynczej kolumnie (kontrahent_id).
Nie pomagają natomiast dla kolumn po prawej stronie, czyli indeks na pojedyncza kolumnę (kiedy) wciąż może być potrzebny.

Jeżeli chodzi o zapytanie, które jak się domyślam ma znajdować dla każdego kontrahenta ostatni rekord według daty w kolumnie kiedy, a jeśli takich rekordów jest więcej to według najwyższego id, przy czym brać pod uwagę tylko rekordy spełniające warunek wydanie>0, to proponowałbym przy założeniu, że id jest kluczem primary, mamy indeksy na (kontrahent_id, id) oraz (kiedy, id), a większość rekordów spełnia warunek wydanie>0, takie rozwiązanie:

  1. SELECT w.id
  2. FROM tr_wydanie w
  3. WHERE id =
  4. ( SELECT id
  5. FROM tr_wydanie wi
  6. WHERE w.kontrahent_id = wi.kontrahent_id
  7. AND wydanie >0
  8. ORDER BY wi.kiedy DESC , wi.id DESC
  9. LIMIT 1 )


W tym przypadku indeks (kontrahent_id, id) będzie wykorzystywany do połączenia z correlated subquery, natomiast indeks (kiedy,id) do ORDER BY kiedy DESC, id DESC, otrzymujemy dość czysty explain:
  1. +----+--------------------+-------+-------+---------------+---------------+---------+------+--------+--------------------------+
  2. | id | select_type | TABLE | type | possible_keys | KEY | key_len | ref | rows | Extra |
  3. +----+--------------------+-------+-------+---------------+---------------+---------+------+--------+--------------------------+
  4. | 1 | PRIMARY | w | INDEX | NULL | kontrahent_id | 8 | NULL | 100000 | USING WHERE; USING INDEX |
  5. | 2 | DEPENDENT SUBQUERY | wi | INDEX | kontrahent_id | kiedy | 12 | NULL | 1 | USING WHERE |
  6. +----+--------------------+-------+-------+---------------+---------------+---------+------+--------+--------------------------+

Na testowej bazie z losowymi rekordami 1k kontrahentów, dla każdego po 100 wpisów, czyli w sumie 100000 rekordów zapytanie działa około 2s, co przy tej strukturze danych i warunkach zapytania jest dość dobrym wynikiem.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.