Query dauert viel zu lange

Ich habe folgendes SQL Statement:

select distinct sr.styleid, sr.gender, se.agemin * 1000 + se.agemax as agegroup 
from swimresult sr
left join swimevent se on se.swimeventid = sr.swimeventid  
left join swimresult sr2 on sr.gender=sr2.gender and sr.styleid=sr2.styleid and sr.course = sr2.course
left join recorditem ri on ri.swimresultid = sr2.swimresultid and ri.recordlistid = 50069 
where sr.meetid = 627794 and sr.place = 1 and sr.styleid <= 20 and se.agemin >= 25 and ri.swimresultid is null

Ich weiss, ein left join ist immer langsam aber über den Filter sr.meetid = 62774 habe ich ein Kriterium das aus der Tabelle mit mehreren Millionen Datensätzen etwa 2000 Datensätze auswählt. Das Query läuft ewigs, nach 15 Minuten habe ich abgebrochen.
Was mache ich falsch?

Christian

Zeig uns das EXPLAIN, und wenn möglich das EXPLAIN ANALYSE dazu. Sowie Tabellendefinitionen aller beteiligten Tabellen.

Das ist das Explain, Analyze:

Unique  (cost=2192078.91..2302029.83 rows=682158 width=8) (actual time=1456116.838..1625111.102 rows=252 loops=1)
  ->  Sort  (cost=2192078.91..2219566.64 rows=10995092 width=8) (actual time=1456116.826..1567525.031 rows=345652197 loops=1)
        Sort Key: sr.styleid, sr.gender, (((se.agemin * 1000) + se.agemax))
        Sort Method: external merge  Disk: 6082384kB
        ->  Hash Anti Join  (cost=447.75..820283.31 rows=10995092 width=8) (actual time=1.892..1136980.974 rows=345652197 loops=1)
              Hash Cond: (sr2.swimresultid = ri.swimresultid)
              ->  Nested Loop Left Join  (cost=1.57..626047.60 rows=10995184 width=12) (actual time=1.028..1041147.024 rows=345656055 loops=1)
                    ->  Nested Loop  (cost=1.00..2234.11 rows=9 width=10) (actual time=0.170..8.058 rows=252 loops=1)
                          ->  Index Scan using ix_swimresult_meet on swimresult sr  (cost=0.57..1771.59 rows=189 width=10) (actual time=0.093..2.300 rows=279 loops=1)
                                Index Cond: ((meetid = 627794) AND (styleid <= 20))
                                Filter: (place = 1)
                                Rows Removed by Filter: 735
                          ->  Index Scan using pk_swimevent on swimevent se  (cost=0.43..2.45 rows=1 width=8) (actual time=0.014..0.014 rows=1 loops=279)
                                Index Cond: (swimeventid = sr.swimeventid)
                                Filter: (agemin >= 25)
                                Rows Removed by Filter: 0
                    ->  Index Scan using ix_swimresult_style on swimresult sr2  (cost=0.57..68364.09 rows=94852 width=10) (actual time=0.077..3893.551 rows=1371651 loops=252)
                          Index Cond: ((sr.course = course) AND (sr.gender = gender) AND (sr.styleid = styleid))
              ->  Hash  (cost=438.21..438.21 rows=638 width=4) (actual time=0.838..0.838 rows=723 loops=1)
                    Buckets: 1024  Batches: 1  Memory Usage: 34kB
                    ->  Bitmap Heap Scan on recorditem ri  (cost=10.23..438.21 rows=638 width=4) (actual time=0.195..0.732 rows=723 loops=1)
                          Recheck Cond: (recordlistid = 50069)
                          Heap Blocks: exact=25
                          ->  Bitmap Index Scan on ix_recorditem_recordlist  (cost=0.00..10.07 rows=638 width=0) (actual time=0.172..0.172 rows=723 loops=1)
                                Index Cond: (recordlistid = 50069)
Planning Time: 3.378 ms
Execution Time: 1625568.634 ms

Die Tabellen SWIMRESULT, SWIMEVENT und RANKINGITEM haben alle ein Primarykey auf dem ID Feld.

Hast Du mal versucht die LEFT JOINs durch NOT EXIST Bedingungen zu ersetzen? Dann kannst Du vermutlich auch das DISTINCT weglassen:

select sr.styleid, sr.gender, se.agemin * 1000 + se.agemax as agegroup 
from swimresult sr
  left join swimevent se on se.swimeventid = sr.swimeventid  
where sr.meetid = 627794 
  and sr.place = 1 
  and sr.styleid <= 20 
  and se.agemin >= 25 
  and not exists (select * 
                  from recorditem ri
                    join swimresult sr2 on ri.swimresultid = sr2.swimresultid
                  where ri.recordlistid = 50069
                    and sr.gender = sr2.gender 
                    and sr.styleid = sr2.styleid 
                    and sr.course = sr2.course)

RANKINGITEM gibt es nicht im Explain / SQL.

Das not exists ist vielleicht die Lösung, aber es wird ja durch die “recht offene” Einschränkung zurück sr gender, styleid und course irgendwie ins Knie getreten.
Überhaupt finde ich den Join über die 3 Felder sehr schwach. Fehlt vielleicht ein join kriterium? Und muss wirklich alles Outer sein?
Und wäre ri.recordlistid = 50069 nicht eher where clause?

Danke für die Hinweise. Ich habe das Problem nun mit zwei separaten SQL und ein paar Zeilen Code gelöst.

Christian

Genereller Tip: DISTINCT möglichst vermeiden!

Warum?

Erstens: weils potentielle Fehler zukleistert. Meist sind Dubletten nämlich ein Zeichen dafür, daß man sich irgendwo verJOINt hat und z.B. ein INNER JOIN setzen sollte oder irgendein Join-Kriterium fehlt.

Zweitens: weil nur wenige RDBMS-Optimizer bei DISTINCT merken, wenn es nutzbare Indices über die fraglichen Spalten gibt. Deshalb ist häufig (nicht immer!) ein GROUP BY die bessere Option.

Also z.B. statt

SELECT DISTINCT ID, NAME FROM PERSON

lieber gruppieren:

SELECT ID,NAME FROM PERSON GROUP BY ID,NAME.

Liefert das gleiche Ergebnis, lotst aber die meisten RDBMS direkt dazu, vorhandene Indices für ID und Name zu nutzen. Bei DISTINCT mußt Du Glück haben daß der Optimizer gut genug ist.

Bei der einen Tabelle bringt das noch nicht wirklich was weil man da eh einen Table Scan braucht, aber wenn man das mit ein paar JOINs kombiniert sieht der EXPLAIN ANALYZE da schnell ganz anders aus.

Bitte überleg nochmal warum das outer joins sein müssen. Ich meine zu glauben, daß Du da mindestens zwei inner joins nehmen kannst.

Es könnte helfen, eine View als Grundlage der Query zu bauen und diese zu indizieren.

Außerdem ist distinct immer ein Bremskandidat. Tuts ein Group by nicht auch?

EXIST oder IN statt LEFT JOIN müßte eigentlich im Zweifelsfall eher noch langsamer sein, weil die Subquery dann auf Row Level wiederholt ausgeführt werden muß.

Dieses Thema wurde automatisch 60 Tage nach der letzten Antwort geschlossen. Es sind keine neuen Antworten mehr erlaubt.