PDA

View Full Version : Langsames SQL wegen großer IN() Anweisung



Seiten : [1] 2

dschroeder
04-04-19, 16:34
Hallo,

hat vielleicht jemand eine Idee, um folgendes SQL Problem zu lösen:

Wir haben eine SQL Abfrage, die per Programm generiert wird (auf Java Seite per Hibernate). Die Abfrage dauert sehr lange (ca. 5 Sekunden), obwohl nur ein paar tausend Datensätze selektiert werden.

Woran das ganze liegt, ist uns schon klar:

Das Statement hat in etwa folgende Form:



select feld1 from tabelle1
join tabelle2 on key1=key2
where tabelle2.nummer in (12345, 24575, 58713, <... 2000 weitere Werte ...>, 87548)


Das ganze geht schnell, wenn die IN-Anweisung nicht zu viele Werte beinhaltet. 20 Werte sind überhaupt kein Problem. Aber wird haben da manchmal über 2000 Werte in der IN-Klausel. Dann dauert das extrem lange.

Visual Explain und der Index Advsior konnten keine Ratschläge geben. Vielleicht hat jemand von Euch ja noch eine Idee, wie man die lange IN-Anweisung anders kodieren könnte.

Dieter

BenderD
04-04-19, 16:40
... wenn die Werte aus einem Resultset stammen, kann man einen inner join draus machen.

dschroeder
04-04-19, 16:48
Vielen Dank für die Anwort.

Ich glaube aber, genau das geht nicht. Die Werte werden, soweit ich weiß, per Programmcode ermittelt.

dschroeder
04-04-19, 16:54
Ich habe eben mal (in einer interaktiven SQL Oberfläche) etwas rumgetestet. Wenn ich die Anweisung so zerlege, dass ich zunächst die Daten nur aus der tabelle2 selektiere und das Ergebnis in eine temporäre Tabelle in QTEMP speichere, dauert das ca. 1 Sekunde.

Wenn ich dann den join der tabelle1 gegen die QTEMP Tabelle laufen lasse, dauert es nochmal ca. 0,3 Sekunden. Insgesamt ist es also deutlich schneller als der Aufruf in einer einzigen Anweisung mit join.

Aber ich habe dann anstatt einer, leider zwei einzelne SQL Anweisungen. Ich weiß nicht, ob das in der Java Anwendung so einfach funktioniert.

Wenn ich quasi genau das gleiche mit einer common table expression mache, dauert es wieder lange (> 5 Sekunden).

xenofob
04-04-19, 18:09
Mit der Abfrage werden denke ich keine Daten gebuffert.
Vielleicht liegt das an der Where-Bedingung im join..

Schon mal das probiert?


select feld1 from tabelle1
where key1 in
(Select key2 from Tabelle2 where tabelle2.nummer in (csvstring<zahlenstring>))</zahlenstring>

B.Hauser
04-04-19, 18:39
Wie sieht es mit den Indices aus?
Habt Ihr einen Index für die 2. Tabelle mit NUMMER (1.Schlüssel) und dann KEY (folg.Schlüssel)?
Wenn nein leg mal an.

Birgitta

andreaspr@aon.at
05-04-19, 07:12
Ich würde es mit einem LEFT JOIN machen und die Reihenfolge des JOINs ändern:


select feld1 from tabelle2
left join tabelle1 on key1=key2
where tabelle2.nummer in (12345, 24575, 58713, <... 2000 weitere Werte ...>, 87548)

lg Andreas

Fuerchau
05-04-19, 07:49
"left" ist der Default, wenn man das beim Join (wie oben gezeigt) weglässt.
Hinzu kommt, dass eigentlich ein Inner-Join benötigt wird, da die Where-Klausel nicht auf NULL abfragt.

Ich stimme da eher Birgitta zu.
Vielleicht kann das dann per "exists" (automatisch) aufgelöst werden.
Der Index kann entweder über "Key2, Number" oder umgekehrt erstellt werden.

Ich kann mir andererseits nicht vorstellen, dass Hibernate hier so schlecht ist. D*B kann da ggf. mehr dazu sagen.
Vielleicht stimmt das Datenmodell nicht ganz?
Außerdem besteht die Gefahr, dass der SQL irgendwann größer 32K wird (falls die Grenze noch besteht).

BenderD
05-04-19, 07:58
... Hibernate ist so gut oder so schlecht wie man es verwendet. Auch hier sitzt das Problem eher vor dem Bildschirm.

xenofob
05-04-19, 09:34
Left join ist definitiv nicht der default join..
Ohne "Schnittmengenrichtungsangabe" (left, right, outer.. ) ist inner der default.

https://stackoverflow.com/questions/565620/difference-between-join-and-inner-join