# Queries Verbesserung



## OnDemand (12. Jul 2021)

Hi zusammen,

hab hier ein paar Queries die übel lahm sind, trotz Indexe usw. 

Hat jemand ne Idee wie man die vereinfachen kann? Mit Join & Co hab ich leider noch keine Erfahrung (wird dann wohl mal Zeit)


```
SELECT * FROM products p WHERE p.webshop_active=true AND p.parent = false AND p.sales_price_calculated IS NOT NULL AND NOT EXISTS (SELECT null FROM products_marketplace_mapping pms WHERE pms.product_id = p.id AND pms.marketplace_id= :marketplaceId) LIMIT 100
```
Ohne die Limiterung dauert das übelst ewig  (dann fehlen natürlich auch Daten)


----------



## mihe7 (12. Jul 2021)

Dürfte dem entsprechen (ungetestet):

```
SELECT * FROM products p 
  LEFT JOIN products_marketplace_mapping pms ON p.id = pms.product_id
  WHERE (pms.product_id is null or pms.marketplace_id= :marketplaceId)
    AND p.webshop_active = true AND p.parent = false
```


----------



## OnDemand (12. Jul 2021)

Ach Gott, das kapiert ich nicht auf anhieb. Da muss ich mich erstmal belesen was LEFT und RIGHT Join ist.
Kann die Geschwindigkeit aber Grundsätzlich mit dem Query zu tun haben?

Hier ist noch so ein Murks. Könntest du mir hier auch helfen? wäre grandio, dann hab ich auch direkt 2 Beispiele die ich mit am WE auseinanderpflücken kann.

```
SELECT * FROM products p, products_variants WHERE p.id = pv.id p.webshop_active=true AND p.parent = true AND pv.sales_price_calculated IS NOT NULL AND NOT EXISTS (SELECT null FROM products_marketplace_mapping pms WHERE pms.product_id = p.id) LIMIT 100
```


----------



## mihe7 (12. Jul 2021)

NicoDeluxe hat gesagt.:


> Da muss ich mich erstmal belesen was LEFT und RIGHT Join ist.


Das ist recht einfach: ein JOIN verknüpft zwei Tabellen ensprechend der ON-Klausel. Eine Tabelle steht links und eine rechts vom JOIN-Operator.

Bei einem INNER JOIN enthält das Ergebnis nur die Zeilen aus beiden Tabellen, die der ON-Klausel entsprechen.
Bei einem LEFT JOIN enthält das Ergebnis in jedem Fall alle Zeilen aus der linken Tabelle. Es kann also Sätze in der rechten Tabelle geben, die der ON-Klausel nicht entsprechen. In dem Fall werden die Spalten der rechten Tabelle im Ergebnis einfach mit NULL belegt.
Bei einem RIGHT JOIN ist es umgekehrt.

Einfach mal einfachen Beispielen ausprobieren, dann siehst Du das sofort.



NicoDeluxe hat gesagt.:


> `SELECT * FROM products p, products_variants WHERE p.id = pv.id p.webshop_active=true AND p.parent = true AND pv.sales_price_calculated IS NOT NULL AND NOT EXISTS (SELECT null FROM products_marketplace_mapping pms WHERE pms.product_id = p.id) LIMIT 100`


Hier fehlt was nach p.id = pv.id. Dieser Vergleich in der WHERE-Klausel entspricht übrigens einem INNER JOIN (`SELECT * FROM products p INNER JOIN products_variants pv ON p.id = pv.id`).


----------



## OnDemand (12. Jul 2021)

mihe7 hat gesagt.:


> Hier fehlt was nach p.id = pv.id


Jo hast Recht, ein AND fehlt.

Das klingt auf den ersten Blick ja erstmal simpel. Galube wenn man es mal kapiert hat, ist es das auch


----------



## OnDemand (12. Jul 2021)

Hab es mal eingebaut, soooooo viel schneller scheint es nicht zu sein. Ich komm nicht mal auf meinen Login so schnell. Das läft übel lange, kann es sein dass der Query so viel Resourcen braucht? Was könnte man in dem Fall machen?

Nur die ID geben lassen und dann jedes Objekt nach Bedarf einzeln holen statt alle in einer List?


----------



## mihe7 (12. Jul 2021)

NicoDeluxe hat gesagt.:


> Das läft übel lange, kann es sein dass der Query so viel Resourcen braucht? Was könnte man in dem Fall machen?


Indizes hast Du auf den betreffenden Spalten?


----------



## OnDemand (12. Jul 2021)

Ja die sind gesetzt soweit. Problem ist glaube der RAM. Der Query liest da 190.000 Objekte aus, die recht viele abhängige Objekte laden. Dadurch explodiert der RAM. Müsste es vermutlich dann doch splitten oder?


----------



## mrBrown (12. Jul 2021)

Setz mal ein `EXPLAIN ANALYZE` vor die Query, damit du überhaupt weißt, was daran langsam ist


----------



## OnDemand (12. Jul 2021)

Öhm das klappt bei mir nicht. Weder mit ANALYZE noch mit EXPLAIN ANALAYZE 
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT * FROM products' at line 1


----------



## mrBrown (12. Jul 2021)

Versuch mal nur `EXPLAIN`, ohne `ANALYZE`.


Vielleicht solltest du die Version mal updaten, die ist ja dann über 2 Jahre alt...


----------



## OnDemand (12. Jul 2021)

ah mit EXPLAIN klappt es. Aber was genau sagt mir das

```
mysql> EXPLAIN SELECT * FROM products p LEFT JOIN products_marketplace_mapping pms ON p.id = pms.product_id WHERE (pms.product_id is null or pms.marketplace_id= 2) AND p.wective = true AND p.parent = false;
+----+-------------+-------+------------+------+-------------------------------------------+-------------------------------------------+---------+--------------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys                             | key                                       | key_len | ref                | rows  | filtered | Extra       |
+----+-------------+-------+------------+------+-------------------------------------------+-------------------------------------------+---------+--------------------+-------+----------+-------------+
|  1 | SIMPLE      | p     | NULL       | ref  | products_idx_webshop_ac_parent_sales_calc | products_idx_webshop_ac_parent_sales_calc | 4       | const,const        | 90782 |   100.00 | NULL        |
|  1 | SIMPLE      | pms   | NULL       | ref  | product_id,product_idVariantMarketplace   | product_id                                | 5       | database.p.id |     1 |   100.00 | Using where |
+----+-------------+-------+------------+------+-------------------------------------------+-------------------------------------------+---------+--------------------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
```


----------



## OnDemand (12. Jul 2021)

mrBrown hat gesagt.:


> Vielleicht solltest du die Version mal updaten, die ist ja dann über 2 Jahre alt...


Kommt auch, gehen dann auf Maria irgendwann dieses Jahr.


----------



## LimDul (12. Jul 2021)

Wieviel Rows soll das Query denn zurückgeben? Und wo wird das Query verwendet? Ist es langsam, wenn du es in MySQL ausführst? Oder kommen da etwa 190.000 Zeilen zurück, die dann in Java-Objekte geladen werden? 

Dann ist nicht das Query das Problem, sondern die Fachlogik - man sollte eigentlich nie so viele komplette Objekt-Geflechte direkt im Speicher laden, das hört sich mehr nach Batch-Verarbeitung oder Co an.


----------



## OnDemand (12. Jul 2021)

Joa das könnte sein, dass das Mapping so lange braucht / viel Recource. Der Query (Spring Boot Repository) baut daraus eine List.

Hab da an dem Produkt einige Sub-Objekte dran. Ist mir schon ein paar Mal auf die Füße gefallen. Wenn die aber von einander ablöse oder aber mit Lazy arbeite hab ich immer das Problem, dass die Sub-Objekte nicht mitgelöscht werden obwohl alles casdiert wird.

Müsste dann also die Objekte wirklich lose von einander machen und wenn jemand ein "Produkt" löscht, dann die Unterobjekte auch löschen "per Hand" statt das vom Hibernate machen zu lassen.

Beispiel:
Produkt
- List<Images>
- List <Attributes>

dabei kann jede List 10 Images haben und 100 Attribute. Das ist dann in der Tat einiges, was da geladen werden muss.

Oder Variante 2 wäre, dass ich mit von dem Query nur eine List mit IDs geben lasse, und die verarbeitende Methode dann jeweils das Produkt per ID holt. Macht zwar viele einzelne Roundtrips, aber dafür nicht so viel Last aufeinmal


----------



## mrBrown (12. Jul 2021)

NicoDeluxe hat gesagt.:


> Joa das könnte sein, dass das Mapping so lange braucht / viel Recource.


Bevor du irgendwo anfängst zu optimieren, solltest du in jedem Fall erstmal rausfinden, an welchen Stellen genau das langsam ist...




NicoDeluxe hat gesagt.:


> ah mit EXPLAIN klappt es. Aber was genau sagt mir das


Oftmals kann man da offensichtliche Probleme erkennen. 

Um da aber in deinem Fall was sinnvolles sagen zu können, bräuchte man vermutlich einmal ein Beispiel, damit man zumindest die Tabelle mit passenden Typen und Indizes etc nachbauen kann.


----------



## LimDul (12. Jul 2021)

mrBrown hat gesagt.:


> Bevor du irgendwo anfängst zu optimieren, solltest du in jedem Fall erstmal rausfinden, an welchen Stellen genau das langsam ist...


Das kann ich nur unterschreiben. Alles andere ist stochern im Nebel. Das wichtigste bei Performance-Problemen ist den Finger genau auf die Stelle zu legen, die das Problem verursacht. Und nach den Indizien hier würde ich nicht das Query als das Problem ansehen sondern die Verarbeitung der Daten. Aber das weiß man genau, wenn man mal gemessen hat.


----------



## mrBrown (12. Jul 2021)

LimDul hat gesagt.:


> Und nach den Indizien hier würde ich nicht das Query als das Problem


Wobei man die Indizes wahrscheinlich schon verbessern könnte, `products_idx_webshop_ac_parent_sales_calc` klingt zumindest so, als hätte es was mit der parent-Column zu tun (ist die einzige, die vom Namen ansatzweise passt), die allerdings ein boolean und damit erstmal komisch ist. Das pms.marketplace_id nicht benutzt wird _könnte_ sich auch negativ auswirken.

Aber dafür müsste man die Tabelle sehen, und Sinn macht es natürlich erst, wenn die Query selbst überhaupt zu langsam ist...


----------



## Thallius (12. Jul 2021)

Wieso führst du den query nicht einfach mal in einem MySQL Management Tool aus. Dann siehst du doch wie lange er braucht


----------



## Barista (12. Jul 2021)

Wenn SQL langsam ist, hilft oft ein Index.


----------

