# SQL Abfrage optimieren



## didi577 (23. Sep 2019)

Hallo,

ich habe hier eine mySQL Abfrage. Ich denke sie kann performanter geschrieben werden. Ich möchte erreichen das NULL Werte mit 0 in der Spalte angezeigt werden. Ich denke das macht es aber langsam. 

```
SELECT t.user22 AS Gruppe, IFNULL(a.Anzahl, 0) AS SecLevel, FORMAT(IFNULL(a.Schnitt, 0), 0) AS Schnitt, IFNULL(b.Anzahl, 0) AS ThiLevel, FORMAT(IFNULL(b.Schnitt, 0), 0) AS Schnitt,  IFNULL(c.Anzahl, 0) AS Gesamt
FROM crmdb.tickets t
LEFT JOIN (SELECT t.user22, COUNT(t.tnumber) AS Anzahl, (SUM(DATEDIFF(curdate(), from_unixtime(t.createtime)))/ COUNT(t.tnumber)) AS Schnitt 
FROM crmdb.tickets t 
WHERE (t.status = 0 OR t.status = 1) 
AND t.tnumber LIKE 'HL-%' 
AND user26 = '' 
GROUP BY t.user22) a ON a.user22 = t.user22
RIGHT JOIN (SELECT t.user22, COUNT(t.tnumber) AS Anzahl, (SUM(DATEDIFF(curdate(), from_unixtime(t.createtime)))/ COUNT(t.tnumber)) AS Schnitt 
FROM crmdb.tickets t 
WHERE (t.status = 0 OR t.status = 1) 
AND t.tnumber LIKE 'HL-%' 
AND user26 != '' 
GROUP BY t.user22) b ON b.user22 = t.user22
RIGHT JOIN (SELECT t.user22, COUNT(t.tnumber) AS Anzahl
FROM crmdb.tickets t 
WHERE (t.status = 0 OR t.status = 1) 
AND t.tnumber LIKE 'HL-%' 
GROUP BY t.user22) c ON c.user22 = t.user22
GROUP BY t.user22
;
```


Kann mir jemand helfen?


----------



## mihe7 (23. Sep 2019)

Wenn ich es richtig sehe, willst Du (Achtung: ungetestet):

```
SELECT user22 AS Gruppe, 
       anzahl_leer AS SecLevel,
       FORMAT(COALESCE(summe_differenz/anzahl_leer, 0), 0) AS Schnitt, 
       anzahl_nicht_leer AS ThiLevel, 
       FORMAT(COALESCE(summe_differenz/anzahl_nicht_leer, 0), 0) AS Schnitt,
       anzahl_gesamt AS Gesamt
  FROM (SELECT user22,
             coalesce(sum(case when user26 = '' then 1 else 0),0) as anzahl_leer,
             coalesce(sum(case when user26 <> '' then 1 else 0), 0) as anzahl_nicht_leer,
             coalesce(sum(number)) as anzahl_gesamt,
             coalesce(sum(datediff(curdate(), from_unixtime(createtime))), 0) as summe_differenz
        FROM crmdb.tickets
       WHERE status in (0,1) and tnumber like 'HL-%'
       GROUP BY user22) t
```


----------



## didi577 (23. Sep 2019)

es kommt leider noch dieser Fehler:

18:50:45    SELECT user22 AS Gruppe,         anzahl_leer AS SecLevel,        FORMAT(COALESCE(summe_differenz/anzahl_leer, 0), 0) AS Schnitt,         anzahl_nicht_leer AS ThiLevel,         FORMAT(COALESCE(summe_differenz/anzahl_nicht_leer, 0), 0) AS Schnitt,        anzahl_gesamt AS Gesamt   FROM (SELECT user22,              coalesce(sum(case when user26 = '' then 1 else 0),0) as anzahl_leer,              coalesce(sum(case when user26 <> '' then 1 else 0), 0) as anzahl_nicht_leer,              coalesce(sum(number)) as anzahl_gesamt,              coalesce(sum(datediff(curdate(), from_unixtime(createtime))), 0) as summe_differenz         FROM crmdb.tickets        WHERE status in (0,1) and tnumber like 'HL-%'        GROUP BY user22) t    Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to *use near '),0) as anzahl_leer,              coalesce(sum(case when user26 <> '' then 1 els' at line 8*    0.078 sec


----------



## mihe7 (23. Sep 2019)

Ach, ich sehe, ich habe das end vergessen und bei coalesce(sum(number)) auch das 0. Probier mal

```
SELECT t.user22 AS Gruppe,
       anzahl_leer AS SecLevel,
       FORMAT(COALESCE(summe_differenz/anzahl_leer, 0), 0) AS Schnitt,
       anzahl_nicht_leer AS ThiLevel,
       FORMAT(COALESCE(summe_differenz/anzahl_nicht_leer, 0), 0) AS Schnitt,
       anzahl_gesamt AS Gesamt
  FROM (SELECT user22,
             coalesce(sum(case when user26 = '' then 1 else 0 end),0) as anzahl_leer,
             coalesce(sum(case when user26 <> '' then 1 else 0 end), 0) as anzahl_nicht_leer,
             coalesce(sum(number),0) as anzahl_gesamt,
             coalesce(sum(datediff(curdate(), from_unixtime(createtime))), 0) as summe_differenz
        FROM crmdb.tickets
       WHERE status in (0,1) and tnumber like 'HL-%'
       GROUP BY user22) t
```
Wenn das nicht funktioniert, kannst Du mal die CREATE-Abfrage zur Erzeugung der Tabelle hier einstellen?


----------



## didi577 (23. Sep 2019)

läuft  jetzt erstmal...sau schnell...vielen Dank

die Spalten Schnitt und Gesamt klemmen noch

das kommt raus:


das muss rauskommen:


----------



## mihe7 (23. Sep 2019)

didi577 hat gesagt.:


> die Spalten Schnitt und Gesamt klemmen noch


Argh, nächster Versuch - ich hoffe, ich habe keine Klammern übersehen.


```
SELECT t.user22 AS Gruppe,
       anzahl_leer AS SecLevel,
       FORMAT(COALESCE(differenz_leer/anzahl_leer, 0), 0) AS Schnitt,
       anzahl_nicht_leer AS ThiLevel,
       FORMAT(COALESCE(differenz_nicht_leer/anzahl_nicht_leer, 0), 0) AS Schnitt,
       anzahl_gesamt AS Gesamt
  FROM (SELECT user22,
             coalesce(sum(case when user26 = '' then 1 else 0 end),0) as anzahl_leer,
             coalesce(sum(case when user26 <> '' then 1 else 0 end), 0) as anzahl_nicht_leer,
             count(number) as anzahl_gesamt,
             coalesce(sum(case when user26 = ''
                 then datediff(curdate(), from_unixtime(createtime))
                 else 0
               end), 0) as differenz_leer,
             coalesce(sum(case when user26 <> ''
                 then datediff(curdate(), from_unixtime(createtime))
                 else 0
               end), 0) as differenz_nicht_leer
        FROM crmdb.tickets
       WHERE status in (0,1) and tnumber like 'HL-%'
       GROUP BY user22) t
```


----------



## didi577 (23. Sep 2019)

cool jetzt passt es

von 2,359 sec auf 0,094 sec ...Respekt

ich schätze "coalesce" statt IFNULL ist die Lösung ??


----------



## mihe7 (23. Sep 2019)

Nein. COALESCE entspricht dem SQL-Standard. Es kann mehrere Argumente entgegennehmen und liefert das erste, das nicht NULL ist. IFNULL() ist dagegen eine mySQL-spezifische Funktion, die das zweite Argument liefert, wenn das erste NULL ist. Im konkreten Fall machen also beide das gleiche. 

Die höhere Geschwindigkeit kommt daher, dass nur einmal durch die Tabelle gegangen werden muss, nur einmal gruppiert wird und keine JOINs, insbesondere keine OUTER JOINs verwendet werden.

Wenn Du den genauen Unterschied sehen willst, kannst Du Dir anschauen, was die DB macht, indem Du Dir den Ausführungsplan anzeigen lässt. Das MySQL Workbench kann Dir das grafisch anzeigen. Auf Textbasis funktioniert das, wenn Du die Abfragen ausführst und einfach EXPLAIN davor schreibst (also EXPLAIN SELECT ...) Die Interpretation ist aber nicht ganz einfach: https://dev.mysql.com/doc/refman/8.0/en/explain-output.html


----------



## didi577 (23. Sep 2019)

vielen Dank für deine Hinweise. ich werde jetzt mal alle meine Abfragen checken


----------



## didi577 (1. Okt 2019)

Hi. Ich habe hier noch so eine Abfrage wo man sicher noch Performance rausholen kann. Im Unterschied zu der oberen Abfrage geht diese über mehrere Tabellen:


```
SELECT DISTINCT t.id, 
             t.tnumber, 
             c.company, 
             t.user29, 
             t.status, 
             IF (d.Zeit IS NULL, f.Zeit, d.Zeit) AS Zeit,
             IFNULL(d.Aktionen, 0) + IFNULL(f.Anrufe, 0) AS Aktionen, 
             IFNULL(z.Aktionen, 0) + IFNULL(f.Anrufe, 0) AS AktGesamt
FROM crmdb.tickets t JOIN crmdb.contacts c ON t.cid = c.id  
        LEFT JOIN (SELECT COUNT(a.id) AS Aktionen, 
                                       a.ticket_id, 
                                       from_unixtime(a.chgtime, '%H:%i') AS Zeit 
                          FROM crmdb.tickets t JOIN crmdb.ticket_actions a ON t.id = a.ticket_id 
                          WHERE a.createuser =59
                          AND from_unixtime(a.action_date, '%Y-%m-%d') = '2019-10-01' 
                          GROUP BY t.tnumber) d ON t.id = d.ticket_id 
        LEFT JOIN (SELECT COUNT(l.ttid) AS Anrufe, 
                                       l.ttid, 
                                       from_unixtime(l.chgtime, '%H:%i') AS Zeit 
                          FROM crmdb.tickets t JOIN crmdb.calls l ON t.id = l.ttid 
                          WHERE l.createuser =59 
                          AND from_unixtime(l.createtime, '%Y-%m-%d') = '2019-10-01' 
                         GROUP BY t.tnumber) f ON t.id = f.ttid 
        LEFT JOIN (SELECT COUNT(a.id) AS Aktionen, 
                                       a.ticket_id 
                          FROM crmdb.tickets t JOIN crmdb.ticket_actions a ON t.id = a.ticket_id 
                          GROUP BY t.tnumber) z ON t.id = z.ticket_id  
WHERE t.id IN (SELECT DISTINCT a.ticket_id  
                         FROM crmdb.ticket_actions a  
                         WHERE a.ticket_id = t.id AND a.createuser =59 
                         AND from_unixtime(a.action_date, '%Y-%m-%d') = '2019-10-01')  
                         OR t.id IN (SELECT DISTINCT l.ttid 
                         FROM crmdb.calls l 
                         WHERE l.ttid = t.id 
                         AND l.createuser =59 
                         AND from_unixtime(l.createtime, '%Y-%m-%d') = '2019-10-01') 
GROUP BY t.tnumber
```

Die Abfrage funktioniert, nur eben langsam... kann mir jemand helfen ?


----------



## mihe7 (1. Okt 2019)

Hm... was soll denn da raus kommen?


----------



## didi577 (2. Okt 2019)

ich frage erstmal anders

wie baue ich eine Abfrage performant über 3 mehrere Tabellen auf ohne mit LEFT JOINS zu arbeiten ?? Dann kann ich das hier erstmal entwirren...


----------



## thecain (2. Okt 2019)

Kommt drauf an, was du als Resultat willst. Left Join ist nicht per Definition böse


----------



## mihe7 (2. Okt 2019)

Eine andere Frage: kann ein Wert in t.number mehrfach vorkommen (gibt es in tickets wenigstens zwei Sätze s1 und s2 mit s1.id<>s2.id und s1.number = s2.number)?


----------



## Meniskusschaden (2. Okt 2019)

didi577 hat gesagt.:


> wie baue ich eine Abfrage performant über 3 mehrere Tabellen auf ohne mit LEFT JOINS zu arbeiten ?? Dann kann ich das hier erstmal entwirren...


Wäre es nicht sinnvoller, das zuerst zu entwirren und dann zu sehen, welche Probleme übrig bleiben?

Man kann noch nicht allzu viel dazu sagen, so lange du die Fragen von @mihe7 nicht beantwortest. Ein Punkt fällt mir allerdings in den Sub-SELECTs der äußeren WHERE-Bedingung auf. Warum fragst du in deren WHERE-Bedingungen noch `t.id` ab? Das erscheint mir überflüssig und für die Performance ist es bestimmt nicht gut, Werte von äußeren Abfragen in WHERE-Bedingungen von Sub-SELECTs hinein zu tragen, die ihrerseits Bestandteil der äußeren WHERE-Bedingung sind. Vielleicht wird es hier weg optimiert, falls es wirklich überflüssig sein sollte. Ich würde es aber lieber gleich vermeiden.


----------



## Thallius (3. Okt 2019)

Sub Selects sollten eh immer die letzte Wahl sein und sind bei guter DB Struktur auch nur selten nötig.


----------



## Meniskusschaden (3. Okt 2019)

Thallius hat gesagt.:


> Sub Selects sollten eh immer die letzte Wahl sein und sind bei guter DB Struktur auch nur selten nötig.


Die Aussage halte ich für zu dogmatisch. Nicht korrelierte Sub-Selects sind in der Regel doch völlig unproblematisch (und bei guter DB Struktur auch oft möglich).


----------



## didi577 (4. Okt 2019)

Die Datenbankstruktur ist vorgegeben. Darauf habe ich keinen Einfluss.

Was ist mein Ziel? Ich möchte eine Auswertung darüber wieviele Aktionen/ Anrufe ein Mitarbeiter an einem Tag je Ticket anlegt (Auswertung einer Hotline).

Zu einem Ticket (Tabelle tickets) gibt es Aktionen (Tabelle ticket_actions) und Anrufe (Tabelle calls). 

Beim Anlegen einer Aktion oder eines Anrufs wird die eindeutige ID des Tickets (id) als ticket_id in ticket_actions und ttid in calls gespeichert.

Die auszugebenen Spalten sind:

tnumber - kommt aus Tabelle tickets und ist das Label des Tickets
company - kommt aus Tabelle contacts und ist als Nummer in Tabelle tickets hinterlegt
user29 - kommt 1:1 aus Tabelle tickets
status - kommt 1:1 aus Tabelle tickets
Zeit - ist der Zeitstempel wann die Aktion oder der Anruf angelegt wurde und kommt aus Tabelle ticket_actions oder calls
Aktionen - ist die Anzahl der Aktionen die der Mitarbeiter (createuser) am Tag zu einem Ticket angelegt hat
AktGesamt ist die Geamtzahl aller Aktionen und Anrufe zu einem Ticket

createuser und createtime gibt es in den Tabellen ticket_actions und calls

Da jede Aktion/ Anruf des createusers aufgeführt werden soll kann die Spalte Aktionen wegfallen da sie ja immer 1 wäre

Ich hoffe ich konnte meinen Plan erläutern.


----------



## Meniskusschaden (4. Okt 2019)

didi577 hat gesagt.:


> Ich hoffe ich konnte meinen Plan erläutern.


Nicht ganz. Die Antwort auf diese Frage von @mihe7:


mihe7 hat gesagt.:


> Eine andere Frage: kann ein Wert in t.number mehrfach vorkommen (gibt es in tickets wenigstens zwei Sätze s1 und s2 mit s1.id<>s2.id und s1.number = s2.number)?


wäre schon sehr interessant.



didi577 hat gesagt.:


> Was ist mein Ziel? Ich möchte eine Auswertung darüber wieviele Aktionen/ Anrufe ein Mitarbeiter an einem Tag je Ticket anlegt (Auswertung einer Hotline).


Das heißt, wenn beispielsweise zwei verschiedene Tickets mit dem identischen Label "Drucker geht nicht" angelegt werden, sollen die Werte der beiden nicht zusammengefasst werden (wie es deine Abfrage aus #10 tun würde), sondern pro Ticket dargestellt werden?
Falls sie doch zusammengefasst werden sollen: welches der beiden Tickets soll dann für Company und Status maßgeblich sein?


----------



## didi577 (4. Okt 2019)

Meniskusschaden hat gesagt.:


> Nicht ganz. Die Antwort auf diese Frage von @mihe7:


ein Wert also eine id kann nur einmal in tnumber vorkommen



Meniskusschaden hat gesagt.:


> Das heißt, wenn beispielsweise zwei verschiedene Tickets mit dem identischen Label "Drucker geht nicht" angelegt werden, sollen die Werte der beiden nicht zusammengefasst werden (wie es deine Abfrage aus #10 tun würde), sondern pro Ticket dargestellt werden?
> Falls sie doch zusammengefasst werden sollen: welches der beiden Tickets soll dann für Company und Status maßgeblich sein?


nein sie sollen nicht zusammengefasst werden

wenn ich unter einer tnumber 3 Aktionen und 2 Anrufe habe sollen mit der tnumber 5 Zeilen mit jeweils der company und dem Status dargestellt werden


----------



## mihe7 (4. Okt 2019)

Also, wenn ich Dich richtig verstehe, willst Du jede Aktion und jeden Anruf einzeln darstellen. Das wäre dann einfach etwa wie:

```
SELECT t.tnumber,
       o.company,
       t.user29,
       t.status,
       from_unixtime(a.chgtime, '%H:%i') AS Zeit,
       'A' -- Action 
  FROM tickets t INNER JOIN ticket_actions a ON t.id = a.ticket_id
 WHERE a.createuser = 59
   AND a.createtime >= unix_timestamp('2019-10-01')
   AND a.createtime < unix_timestamp('2019-10-02')
UNION
SELECT t.tnumber,
       o.company,
       t.user29,
       t.status,
       from_unixtime(c.chgtime, '%H:%i') AS Zeit,
       'C' -- Call
  FROM tickets t INNER JOIN calls c ON t.id = c.ttid
 WHERE c.createuser = 59
   AND c.createtime >= unix_timestamp('2019-10-01')
   AND c.createtime < unix_timestamp('2019-10-02')
```


----------



## mihe7 (4. Okt 2019)

Nachtrag: bitte UNION ALL statt UNION verwenden.


----------



## didi577 (5. Okt 2019)

ja so wollte ich es haben, Danke

wenn ich jetzt o.company noch mit ermitteln will habe ich so angepasst:

```
SELECT t.tnumber,
       o.company,
       t.user29,
       t.status,
       from_unixtime(a.chgtime, '%H:%i') AS Zeit,
       'A' -- Action 
  FROM crmdb.tickets t INNER JOIN crmdb.ticket_actions a ON t.id = a.ticket_id INNER JOIN crmdb.contacts o ON t.cid = o.id
 WHERE a.createuser = 59
   AND a.createtime >= unix_timestamp('2019-10-01')
   AND a.createtime < unix_timestamp('2019-10-02')
UNION ALL
SELECT t.tnumber,
       o.company,
       t.user29,
       t.status,
       from_unixtime(c.chgtime, '%H:%i') AS Zeit,
       'C' -- Call
  FROM crmdb.tickets t INNER JOIN crmdb.calls c ON t.id = c.ttid INNER JOIN crmdb.contacts o ON t.cid = o.id
 WHERE c.createuser = 59
   AND c.createtime >= unix_timestamp('2019-10-01')
   AND c.createtime < unix_timestamp('2019-10-02')
```

es funktioniert, ist aber wieder um ein vielfaches langsamer geworden, ist das normal?

in der WHERE Bedingung nutzt du unix_timestamüp statt from_unixtime...bringt das Vprteile??


----------



## didi577 (5. Okt 2019)

...als letzte Spalte wollte ich auch noch die Gesamtzahl aller Aktionen und Anrufe zu dieser tnumber dargestellt haben


----------



## mihe7 (5. Okt 2019)

didi577 hat gesagt.:


> wenn ich jetzt o.company noch mit ermitteln will


Oops, die habe ich ganz vergessen  



didi577 hat gesagt.:


> ist aber wieder um ein vielfaches langsamer geworden, ist das normal?


Was heißt "um ein Vielfaches langsamer"? Der zusätzliche JOIN mach natürlich einen Unterschied, der sollte sich aber in Grenzen halten. 



didi577 hat gesagt.:


> in der WHERE Bedingung nutzt du unix_timestamüp statt from_unixtime...bringt das Vprteile??


Sollte. Erstens muss so nur zweimal ein fixes Datum in den Zeitstempel umgewandelt werden, zweitens kann so der Index verwendet werden, den Du ggf. auf createtime hast. Probiers einfach aus bzw. lass Dir den Ausführungsplan anzeigen.

Führ mal die beiden Abfragen aus:

```
EXPLAIN SELECT chgtime FROM ticket_actions WHERE createuser = 59 AND createtime >= unix_timestamp('2019-10-01') AND createtime < unix_timestamp('2019-10-02');

EXPLAIN SELECT chgtime FROM ticket_actions WHERE createuser = 59 AND from_unixtime(createtime, '%Y-%m-%d') = '2019-10-01' ;
```
Sofern Du einen Unterschied erkennst, kannst Du das Ergebnis ja mal posten.



didi577 hat gesagt.:


> ...als letzte Spalte wollte ich auch noch die Gesamtzahl aller Aktionen und Anrufe zu dieser tnumber dargestellt haben


D. h. es gibt zu einer Ticket-Number mehrere Ticket-IDs?


----------



## didi577 (5. Okt 2019)

mihe7 hat gesagt.:


> D. h. es gibt zu einer Ticket-Number mehrere Ticket-IDs?


jede Ticketnummer hat eine t.id aber es kann zu einer t.id  mehrere a.ticket_id und/oder mehrere c.ttid geben, da es zu einem Ticket mehrere Aktionen und/oder Anrufe geben kann



mihe7 hat gesagt.:


> Was heißt "um ein Vielfaches langsamer"? Der zusätzliche JOIN mach natürlich einen Unterschied, der sollte sich aber in Grenzen halten.


ohne den zusätzlichen JOIN ca. 0,05 sec mit dem JOIN ca. 2,3 sec, wobei ich nicht weiß wie sich schnell und langsam definieren



mihe7 hat gesagt.:


> Sofern Du einen Unterschied erkennst, kannst Du das Ergebnis ja mal posten.


es ist kein Unterschied im Ergebnis nur in der Syntax


----------



## mihe7 (5. Okt 2019)

didi577 hat gesagt.:


> ede Ticketnummer hat eine t.id aber es kann zu einer t.id mehrere a.ticket_id und/oder mehrere c.ttid geben, da es zu einem Ticket mehrere Aktionen und/oder Anrufe geben kann


Mach einfach jeweils ein zusätzliche Spalte `count(*) as Anzahl` dran.

Unsinn. Machen wir später.




didi577 hat gesagt.:


> ohne den zusätzlichen JOIN ca. 0,05 sec mit dem JOIN ca. 2,3 sec, wobei ich nicht weiß wie sich schnell und langsam definieren


Kommt mir viel zu hoch vor. Kannst Du mal ein EXPLAIN davor schreiben und das Ergebnis posten?


----------



## mihe7 (5. Okt 2019)

didi577 hat gesagt.:


> es ist kein Unterschied im Ergebnis nur in der Syntax


Das kann verschiedene Gründe haben. Was steht bei possible_keys bzw. bei key?


----------



## didi577 (6. Okt 2019)

mihe7 hat gesagt.:


> Kommt mir viel zu hoch vor. Kannst Du mal ein EXPLAIN davor schreiben und das Ergebnis posten?





mihe7 hat gesagt.:


> Das kann verschiedene Gründe haben. Was steht bei possible_keys bzw. bei key?


beides ist null


----------



## mihe7 (6. Okt 2019)

Vermutung: Du hast keine Indizes auf createuser und createtime. D. h. um z. B. aus ticket_actions die betreffenden Sätze zu ermitteln, müsste die gesamte Tabelle durchlaufen werden. Da in der Tabelle vermutlich mehr Sätze stehen als in tickets wird das geringere Übel gewählt und durch über alle Tickets iteriert. 

Kannst/Darfst Du Indizes für createuser und ggf. createtime anlegen und den Spaß wiederholen?


----------



## didi577 (6. Okt 2019)

Ne an der DB darf ich gar nichts machen. ich habe lediglich lesenden Zugriff auf ein paar Tabellen



mihe7 hat gesagt.:


> Unsinn. Machen wir später.


hier muss wahrscheinlich noch eine Unterabfrage über ticket_actions und calls mit Gruppierung nach tnumber laufen ?


----------



## mihe7 (6. Okt 2019)

didi577 hat gesagt.:


> hier muss wahrscheinlich noch eine Unterabfrage über ticket_actions und calls mit Gruppierung nach tnumber laufen ?


Wenn Du diese unabhängig von createuser/createtime haben willst, ja. Da muss die DB dann sowieso durch alle Actions und alle Calls laufen, so dass ein Index auf createuser/createtime eh nichts mehr nutzen würde. Mit etwas Glück dreht sich Dein Ausführungsplan nur.


----------



## didi577 (6. Okt 2019)

mihe7 hat gesagt.:


> Wenn Du diese unabhängig von createuser/createtime haben willst, ja


ja so ist es, ich werde mal was probieren ...


----------



## Meniskusschaden (6. Okt 2019)

didi577 hat gesagt.:


> Ne an der DB darf ich gar nichts machen. ich habe lediglich lesenden Zugriff auf ein paar Tabellen


Allerdings zeigt die Abfrage von @mihe7 aus #21, dass man performant an die beteiligten Ticket-Ids kommen kann und für die gibt es ja wohl Indizes in den Tabellen für Aktionen und Anrufe (oder ist das nicht reproduzierbar schnell, sondern vielleicht nur Folge von Caching-Effekten aus vorhergehenden Abfragen?). Vielleicht funktioniert es, die Ticket-Ids mit einer inneren Abfrage auszuwählen und die Ergebnismenge dann in einer äußeren Abfrage per Join über die Ticket-ID mit den fehlenden Daten anzureichern.

Eventuell war auch die Idee deiner ursprünglichen Abfrage aus #10 gar nicht so schlecht. Wie läuft die denn, wenn du sie mit den Erkenntnissen aus diesem Thread vereinfachst? Du könntest z.B. die Abfrage der Ticket-ID aus den WHERE-Bedingungen der beiden Sub-Selects der äußeren WHERE-Bedingung entfernen und die Sub-Selects innerhalb der LEFT JOINs könnten auch direkt (ohne JOIN) auf die jeweilige Tabelle zugreifen, wenn du dort nicht nach tnumber, sondern nach der Ticket-ID gruppierst.


----------



## mihe7 (6. Okt 2019)

Meniskusschaden hat gesagt.:


> dass man performant an die beteiligten Ticket-Ids kommen kann und für die gibt es ja wohl Indizes in den Tabellen für Aktionen und Anrufe (oder ist das nicht reproduzierbar schnell, sondern vielleicht nur Folge von Caching-Effekten aus vorhergehenden Abfragen?).


Warum der zusätzliche JOIN so viel länger brauchen soll, ist mir auch nicht klar. 

Wenn ich mir #25 und seine Antwort aus #29 ansehe, würde ich mal schlussfolgern, dass es keinen Index auf createuser gibt. Für ein `FROM ticket_actions a WHERE a.createuser=59` muss die DB somit alle Aktionen durchlaufen. 

Im Fall des JOINs mit tickets kann man mal davon ausgehen, dass es mehr Aktionen als Tickets gibt. Damit wäre es prinzipiell nachvollziehbar, dass die DB erst über die Tickets läuft und dann die Aktionen dazu nimmt. 

Andersrum wäre in dem Fall aber besser, da sowieso sämtliche Aktionen durchlaufen werden müssen und damit nicht jedes Ticket und jede Firma dazugenommen werden muss, sondern nur die, für die die WHERE-Klausel stimmt.



Meniskusschaden hat gesagt.:


> wenn du dort nicht nach tnumber, sondern nach der Ticket-ID gruppierst.


Das hört sich seeehr vernünftig an  

Ich muss sagen, dass es deutlich einfacher ist, Abfragen zu optimieren, wenn man vor der DB sitzt...  Da probiert man das ein oder andere aus und sieht sofort, was rauskommt.


----------



## Thallius (7. Okt 2019)

Oder der TO bastelt schnell ein sqlFiddle aus seinen Tabellen. Dann wird es für uns auch viel leichter.


----------

