JPQL query braucht zu lange

I

imox

Aktives Mitglied
Hey Leute,

ich hab ein Problem mit meinem JPQL query das sieht aktuell so aus:

SELECT DISTINCT report FROM Report report LEFT JOIN report.tags tag WHERE report.type = :type AND (report.fId = :fId0 ) AND (report.tags in :tags OR report.tags IS EMPTY) ORDER BY report.date DESC

Das filtern der tags ist kein Problem, sobald ich aber "report.tags IS EMPTY" hinzufüge wird mein query extrem langsam und dauert ca. 25 Sekudnen. Normal dauert die ganze abfrage ca. 40ms.

Hat jemand eine Idee ob da evtl. was falsch ist? und wenn nein was ich machen könnte?

Vielen Dank schon mal
 
Thallius

Thallius

Top Contributor
Also wenn Du nur report.tags in :tags schreibst ist es schnell (Ich gehe mal davon aus in tags steht auch was drin) und wenn du is empty schreibst wird es langsam?

Ich kenne mich nicht wirklich aus mit JPQL aber wenn du einen LEFT JOIN ohne ON Klausel in anderen SQL DB's machst, dann joined er die ganze Tabelle zu jeder Zeile, was bedeutet, du bekommst count(tags) * count(report) Zeilen als Antwort, was in 99% der Fälle nicht gewollt ist. Wenn er dann diese viel zu große Anzahl mit dem WHERE filtern muss, könnte es schon langsam werden.

Gruß

Claus
 
mihe7

mihe7

Top Contributor
@Thallius In JPQL sind die Beziehungen in den Entities gegeben, daher braucht es hier kein ON.

@imox mich wundert vielmehr, dass die Query überhaupt läuft, denn report.tags dürfte ja eine Collection sein und IN verlangt auf der linken Seite einen Pfad zu einem "Einzelwert". D. h. da sollte schon mal "tag IN :tags" stehen. Wenn das nichts bringt, dann zeig mal bitte das resultierende SQL.
 
I

imox

Aktives Mitglied
@mihe7 ja du hast recht das stimmte nicht ganz sorry.

SELECT DISTINCT report FROM Report report LEFT JOIN report.tags tags WHERE report.type = :type AND (report.fId = :fId0 ) AND (report.tags in :tags) ORDER BY report.date DESC


Ich sehe auch gerade dass ich vorher das query für die tags quasi selber gebaut hatte was dann so aussieht:

SELECT DISTINCT report FROM Report report LEFT JOIN report.tags tag WHERE report.type = :type AND (report.fId = :fId0 ) AND (tag.id = :tag0 OR tag.id = :tag1 ) ORDER BY report.date DESC

Also mit ner for schleife über die tags und einfach einzeln hinzugefügt. Und dass ist jetzt erstaunlicherweise auch schneller als als wenn ich das mit (report.tags in :tags) mache.

Und ja richtig ist eine collection. Und das funktioniert auch. aber sobald ich dann das IS EMPTY hinzufüge:

SELECT DISTINCT report FROM Report report LEFT JOIN report.tags tag WHERE report.type = :type AND (report.fId = :fId0 ) AND (tag.id = :tag0 OR tag.id = :tag1 OR report.tags IS EMPTY) ORDER BY report.date DESC

wird es extrem langsam.



Grad noch mal geschaut. So funktionierts jetzt mit IN auch besser ;)

SELECT DISTINCT report FROM Report report LEFT JOIN report.tags tags WHERE report.type = :type AND (report.fId = :fId0 ) AND (tags IN :tags) ORDER BY report.date DESC

was komischerweise nicht funktioniert ist jetzt dass direkt die tags anspreche. also das hier:

SELECT DISTINCT report FROM Report report LEFT JOIN report.tags tags WHERE report.type = :type AND (report.fId = :fId0 ) AND (tags in :tags OR (tags IS EMPTY)) ORDER BY report.date DESC

Also "tags IS EMPTY" funktioniert nicht. was ich gerade nicht so ganz verstehe warum? vielleicht würde es dann auch schnell gehen wenn das klappt? Kommt halt eine exception mit syntax error

The collection-valued path 'tags IS EMPTY' must resolve to an association field.
 
Zuletzt bearbeitet:
I

imox

Aktives Mitglied
aber tags ist doch die collection. deswegen funktioniert doch "tags in :tags" auch. und wenn ich report.tags schreibe dann wirds langsamer und wenn ich report.tags IS EMPTY schreibe wirds halt richtig langsam von 20-40ms hoch auf 10-25 sekunden.
 
Thallius

Thallius

Top Contributor
Wer erstellt in so einem FW eigentlich dann die Tabellen und vor allem die Indizes? Ich finde das befremdend, dass da anscheinend irgendwelche DB Logik von dem FW erstellt wird und man selber gar nicht mehr weiß was seine DB eigentlich macht, bzw. wie Sie aufgebaut ist.

In meiner DB würde ich jetzt einfach einen Describe mache und wüßte wo das <Problem liegt. Gibts hier sowas nicht auch?
 
mihe7

mihe7

Top Contributor
Wer erstellt in so einem FW eigentlich dann die Tabellen und vor allem die Indizes? Ich finde das befremdend, dass da anscheinend irgendwelche DB Logik von dem FW erstellt wird und man selber gar nicht mehr weiß was seine DB eigentlich macht, bzw. wie Sie aufgebaut ist.

In meiner DB würde ich jetzt einfach einen Describe mache und wüßte wo das <Problem liegt. Gibts hier sowas nicht auch?
Der Punkt ist, dass man in JPQL eben nicht mit Tabellen sondern mit Objekten arbeitet. Die Abbildung auf das DB-Schema kann man angeben und folgt ansonsten natürlich bestimmten Regeln. Das Describe kannst Du auf der DB natürlich nach wie vor machen. Die "Tabellen" in JPQL sind aber keine Tabellen sondern eben Entities. Für JPQL braucht es kein Describe, denn die Klassen und Attribute hat er ja im Quelltext schon gegeben.
 
Thallius

Thallius

Top Contributor
Der Punkt ist, dass man in JPQL eben nicht mit Tabellen sondern mit Objekten arbeitet. Die Abbildung auf das DB-Schema kann man angeben und folgt ansonsten natürlich bestimmten Regeln. Das Describe kannst Du auf der DB natürlich nach wie vor machen. Die "Tabellen" in JPQL sind aber keine Tabellen sondern eben Entities. Für JPQL braucht es kein Describe, denn die Klassen und Attribute hat er ja im Quelltext schon gegeben.

Soweit ok, aber warum muss er dann beim query noch selber einen join machen? Das sollte Dochten auch automatisch funktioniere oder? Bzw. woher weiß man das man den join selber mache muss?
 
I

imox

Aktives Mitglied
@mihe7 danke dir für die Erklärung :) Hast du noch eine Idee? bzw. wenn ich den join mache report.tags tags ist ja tags dann die collection wieso funktioniert "tags IN :tags" ? aber "tags IS EMPTY" nicht. "report.tags IN :tags" funktioniert ja auch genau so wie "tags IN :tags" und "report.tags IS EMPTY" funktioniert ja auch. und was mich wunder warum "tags IN :tags" schneller ist als "report.tags IN :tags". hoffe nicht ganz so verwirrden xD.

@Thallius Woher man das weiß? Naja das kannste in der doku nachlesen einfach nach JPQL googeln.
 
mihe7

mihe7

Top Contributor
Soweit ok, aber warum muss er dann beim query noch selber einen join machen? Das sollte Dochten auch automatisch funktioniere oder?
Zum Beispiel: INNER JOIN vs LEFT JOIN. Es gibt auch noch FETCH JOINs, dann wird die zurückgegebene Collection beschränkt.

Bzw. woher weiß man das man den join selber mache muss?
Gehen wir mal von aus, dass Report eine Collection von Tag-Objekten enthält und jedes Tag einen Namen hat. Wenn Du mal von Java-Properties ausgehst, dann würde report.getTags() etwas wie Collection<Tag> liefern. Dann gibt aber report.getTags().getName() keinen Sinn, denn getName() würde sich auf die Collection und nicht auf das einzelne Tag-Objekte beziehen.

So verhält es sich eben in JPQL auch: report.tags ist die Collection als Ganzes und report.tags.name gibt es nicht. Um ein einzelnes Element einer Collection darzustellen wird eine Identifikationsvariable verwendet. Das fängt schon beim FROM an:

Bei "FROM Reports" bezeichnet Reports ja kein einzelnes Objekt sondern die komplette Collection. In der WHERE-Klausel willst Du aber auf Attribute eines einzelnen Objekts zugreifen, daher gibt es die ID-Variable: "FROM Reports r". Dabei stellt r nun ein Objekt der Collection dar und Dinge wie "r.name", "r.tags", "r.creator.name" geben Sinn.

Natürlich gibt es Abfragen, wo man sich auf eine Collection als Ganzes bezieht: IS EMPTY zum Beispiel. Hier wird eine Collection erwartet, also ist "report.tags" richtig.

Hast du noch eine Idee?
In Bezug worauf?

wenn ich den join mache report.tags tags ist ja tags dann die collection wieso funktioniert "tags IN :tags" ? aber "tags IS EMPTY" nicht. "report.tags IN :tags" funktioniert ja auch genau so wie "tags IN :tags" und "report.tags IS EMPTY" funktioniert ja auch. und was mich wunder warum "tags IN :tags" schneller ist als "report.tags IN :tags". hoffe nicht ganz so verwirrden xD.
Genau aus den oben genannten Gründen: tags ist ein Element der Collection report.tags und für dieses eine Element kann ich prüfen, ob es in einer "Menge" gegebener Elemente :tags enthalten ist (tags IN :tags). Umgekehrt funktioniert tags IS EMPTY nicht, weil tags keine Collection ist.

Zum Thema "report.tags IN :tags" funktioniert ja auch: nicht in Standard-JPQL.
 
I

imox

Aktives Mitglied
na auf die eigentlich Frage ;) warum ist es so extrem langsamer wenn ich report.tags IS EMPTY hinzufüge. Bzw. hast du eine idee wie das eine normale Geschwindigkeit erlangt? Also darum gehts ja überhaupt ;) Die andern Fragen kamen von anderen usern.

ob es in einer "Menge" gegebener Elemente :tags enthalten ist (tags IN :tags).
hmmm ja logisch ^^ *facepalm* nur ein kleiner Denkfehler. Aber generell weiß ich das ja alles was wir hier besprechen und hab auch schon viele Dokus dazu gelesen. Aber hier komme ich gerade nicht weiter. Ich will halt ungern mir immer alle Objekte holen und das "filtern" in java machen. Wobei ich bezweifle dass das dann überhaupt schneller ist.
 
I

imox

Aktives Mitglied
ups sorry. hmmm weiß grad gar nicht mehr wie ich mir das ausgeben kann? Ich benutze eclipselink. Weisst du wie?
 
mihe7

mihe7

Top Contributor
In die persistence.xml:
XML:
<property name="eclipselink.logging.level" value="FINE"/>
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
(letzte Zeile ist optional) aufnehmen.
 
I

imox

Aktives Mitglied
mit IS EMPTY

SELECT DISTINCT t1.id AS a1, t1.date AS a2, t1.f_id AS a3, t1.type AS a4 FROM tbl_documentation_reports t1 LEFT OUTER JOIN (tbl_documentation_report_tags t2 JOIN tbl_documentation_tags t0 ON (t0.id = t2.f_tag)) ON (t2.f_report = t1.id) WHERE (((t1.type = ?) AND (t1.f_id = ?)) AND ((t0.id IN (?,?)) OR ((SELECT COUNT(t3.id) FROM tbl_documentation_report_tags t4, tbl_documentation_tags t3 WHERE ((t4.f_report = t1.id) AND (t3.id = t4.f_tag))) = ?))) ORDER BY t1.date DESC LIMIT ?, ?
bind => [DAILY, 741, 965230, 2790201, 0, 0, 10]



ohne IS EMPTY
SELECT DISTINCT t1.id AS a1, t1.date AS a2, t1.f_id AS a3, t1.type AS a4 FROM tbl_documentation_reports t1 LEFT OUTER JOIN (tbl_documentation_report_tags t2 JOIN tbl_documentation_tags t0 ON (t0.id = t2.f_tag)) ON (t2.f_report = t1.id) WHERE (((t1.type = ?) AND (t1.f_id = ?)) AND (t0.id IN (?,?))) ORDER BY t1.date DESC LIMIT ?, ?
bind => [DAILY, 741, 965230, 2790201, 0, 10]
 
I

imox

Aktives Mitglied
@mihe7 Na logischerweise EclipseLink ;-) ?Dass hab ich doch nicht geschrieben ^^ anyway, was schlägst als Lösung vor?
 
Thallius

Thallius

Top Contributor
Puh also wenn aus so einfachen Abfragen solche unsinnigen query Monster werden und man das normalerweise nichtmal mitbekommt solange alles funktioniert wie man will, dann wundert mich überhaupt nicht mehr, dass heutzutage Software so unglaublich langsam ist.

Da bleib ich doch lieber bei meinem old school und schreibe die queries selber. Da bin ich ja 100x schneller unterwegs.
 
I

imox

Aktives Mitglied
@Thallius toll toll aber was ist jetzt die Lösung. Darum gehts hier doch und nicht über API's zu meckern ;-) dass das nicht toll ist gebe ich dir ja recht.
 
mihe7

mihe7

Top Contributor
Natürlich warst das nicht Du. Streng genommen weiß man gar nicht, wem man den Vorwurf machen muss: EclipseLink oder der DB. Ich finde es aber befremdlich, einen count() zu machen und dann auch noch mit einer Variablen zu vergleichen, obwohl dahinter eine Konstante steht. Ohne den konkreten Wert der Variablen zu berücksichtigen, lässt die Abfrage gar nichts anderes zu, als erstmal alles zu zählen und dann mit dem Wert zu vergleichen.

Probier mal
SQL:
SELECT DISTINCT report FROM Report report LEFT JOIN report.tags tag WHERE report.type = :type AND (report.fId = :fId0 ) AND (tag in :tags OR NOT EXISTS (SELECT 1 FROM Report r  JOIN report.tags rt WHERE report.fld = r.fld)) ORDER BY report.date DESC
und zeig mal das resultierende SQL.
 
mihe7

mihe7

Top Contributor
Puh also wenn aus so einfachen Abfragen solche unsinnigen query Monster werden und man das normalerweise nichtmal mitbekommt solange alles funktioniert wie man will, dann wundert mich überhaupt nicht mehr, dass heutzutage Software so unglaublich langsam ist.
Ja, leider. Allerdings weißt Du bei SQL auch nicht, was die DB am Ende daraus macht. Früher hatte ich z. B. Oracle-DBs, die haben sehr gut optimiert (es gab ganz wenige Fälle, wo man mal Hints setzen musste). Dagegen ist z. B. mySQL absolut bescheiden, insbesondere was GROUP BY Subqueries betrifft: erst wird der GROUP BY ausgeführt und anschließend verknüpft. Hurra...
 
I

imox

Aktives Mitglied
SELECT DISTINCT t1.id AS a1, t1.date AS a2, t1.f_id AS a3, t1.type AS a4 FROM tbl_documentation_reports t1 LEFT OUTER JOIN (tbl_documentation_report_tags t2 JOIN tbl_documentation_tags t0 ON (t0.id = t2.f_tag)) ON (t2.f_report = t1.id) WHERE (((t1.type = ?) AND (t1.f_id = ?)) AND ((t0.id IN (?,?)) OR NOT EXISTS (SELECT ? FROM tbl_documentation_report_tags t5, tbl_documentation_reports t4, tbl_documentation_tags t3 WHERE ((t1.f_id = t4.f_id) AND ((t5.f_report = t1.id) AND (t3.id = t5.f_tag)))) )) ORDER BY t1.date DESC LIMIT ?, ?
bind => [DAILY, 741, 0, 2790201, 1, 0, 10]


funktioniert aber dauert genau so lange :(


Ich raffs einfach nicht dass man in die Collection reingeht und check ob die objecte dort drin sind 10000 mal schneller ist als zu checken ob die collection leer ist. dass ist für mich irgendwie widersprüchlich, aber jaaaa scheint ja nur die richtige syntax zu fehlen.
 
Zuletzt bearbeitet:
mihe7

mihe7

Top Contributor
funktioniert aber dauert genau so lange
Das sollte nicht sein. Es wird zwar fleißig gejoined, aber nach der ersten Zeile steht fest, dass NOT EXISTS false ist. Die Indizes sind alle vorhanden?

Du kannst Dir auch mal ansehen, was die DB daraus macht, indem Du
SQL:
EXPLAIN SELECT DISTINCT t1.id AS a1, t1.date AS a2, t1.f_id AS a3, t1.type AS a4 FROM tbl_documentation_reports t1 LEFT OUTER JOIN (tbl_documentation_report_tags t2 JOIN tbl_documentation_tags t0 ON (t0.id = t2.f_tag)) ON (t2.f_report = t1.id) WHERE (((t1.type = 'DAILY') AND (t1.f_id = 741)) AND ((t0.id IN (0,2790201)) OR NOT EXISTS (SELECT 1 FROM tbl_documentation_report_tags t5, tbl_documentation_reports t4, tbl_documentation_tags t3 WHERE ((t1.f_id = t4.f_id) AND ((t5.f_report = t1.id) AND (t3.id = t5.f_tag)))) )) ORDER BY t1.date DESC LIMIT 0, 10
direkt in MySQL ausführst (wenn Du das Workbench benutzt, kannst Du Dir den Execution Plan auch anders anzeigen lassen, ich glaube, das geht sogar irgendwie graphisch).
 
I

imox

Aktives Mitglied
Das sollte nicht sein. Es wird zwar fleißig gejoined, aber nach der ersten Zeile steht fest, dass NOT EXISTS false ist. Die Indizes sind alle vorhanden?

hmmm gute Frage ^^ grad nachgeschaut die sind nur auf den ID. Wo sollten die denn noch sein?


Die SQL Abfrage dauert 3,6 ms und spuckt das aus:

1 PRIMARY t1 NULL ALL NULL NULL NULL NULL 43020 1.00 Using where; Using temporary; Using filesort
1 PRIMARY t2 NULL ALL NULL NULL NULL NULL 15806 100.00 Using where; Distinct
1 PRIMARY t0 NULL eq_ref PRIMARY PRIMARY 8 portal.t2.f_tag 1 100.00 Using where; Using index; Distinct
2 DEPENDENT SUBQUERY t5 NULL ALL NULL NULL NULL NULL 15806 10.00 Using where
2 DEPENDENT SUBQUERY t3 NULL eq_ref PRIMARY PRIMARY 8 portal.t5.f_tag 1 100.00 Using where; Using index
2 DEPENDENT SUBQUERY t4 NULL ALL NULL NULL NULL NULL 43020 10.00 Using where; Using join buffer (Block Nested Loop)
 
I

imox

Aktives Mitglied
@mihe7 ok sorry ich hab wohl irgendwas falsch gemacht. auf jeden fall gehts jetzt alles und es ist auch super schnell. Ich danke dir echt vielmals :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
J [JPA] Overlaps mit JPQL darstellen Allgemeine Java-Themen 2
W sql Query abspeichern selber variabele hilfe...? Allgemeine Java-Themen 16
A Variable Parameterinhalte an einen Query übergeben? Allgemeine Java-Themen 3
E SQL Query optimal zusammenbauen? Allgemeine Java-Themen 3
L SQL problem bei der query Allgemeine Java-Themen 3
M Was braucht man, um einen Java Job zu bekommen? Allgemeine Java-Themen 8
R Test Umgebung für Datenbank erstellen, was braucht es? Allgemeine Java-Themen 14
Thallius Wie mache ich eine Java App mit Icon startbar die mehr Heap Speicher braucht? Allgemeine Java-Themen 3
J Ein blutiger Anfänger braucht Hilfe Allgemeine Java-Themen 7
W Simulation - Anfänger braucht Hilfe Allgemeine Java-Themen 14
Antoras Braucht ihr Datenmodellierung? Allgemeine Java-Themen 20
G RXTX library braucht sehr lange zum laden. Ist das normal? Allgemeine Java-Themen 8
B lookupPrintServices braucht eeeeeeeeeeeewig Allgemeine Java-Themen 3
V Eclipse braucht ewig zum Starten meines Codes Allgemeine Java-Themen 21
B Fehlermeldungen die keiner braucht ... Allgemeine Java-Themen 17
M file.delete() braucht ewig Allgemeine Java-Themen 3
H wie viel speicher braucht eigentlich ein array? Allgemeine Java-Themen 2
B Braucht man die neue VM 1.5 Allgemeine Java-Themen 3
P Welches JRE braucht meine Applikation? Allgemeine Java-Themen 3

Ähnliche Java Themen

Anzeige

Neue Themen


Oben