2012-01-18 17 views
5

Supponiamo di avere una query come questa ...Come faccio a dire a MySQL Optimizer di usare l'indice su una tabella derivata?

SELECT T.TaskID, T.TaskName, TAU.AssignedUsers 
FROM `tasks` T 
    LEFT OUTER JOIN (
     SELECT TaskID, GROUP_CONCAT(U.FirstName, ' ', 
      U.LastName SEPARATOR ', ') AS AssignedUsers 
     FROM `tasks_assigned_users` TAU 
      INNER JOIN `users` U ON (TAU.UserID=U.UserID) 
     GROUP BY TaskID 
    ) TAU ON (T.TaskID=TAU.TaskID) 

Più persone possono essere assegnati a un determinato compito. Lo scopo di questa ricerca è quello di mostrare una riga per ogni compito, ma con le persone assegnate al compito in una singola colonna

Ora ... si supponga di avere la messa a punto indici corretti sulla tasks, users e tasks_assigned_users. MySQL Optimizer non utilizzerà ancora l'indice TaskID quando si aggiunge tasks alla tabella derivata. WTF?!?!?

Quindi, la mia domanda è ... come si può fare questa query utilizzare l'indice su tasks_assigned_users.TaskID? Le tabelle temporanee sono zoppe, quindi se questa è l'unica soluzione ... l'ottimizzatore MySQL è stupido.

indici utilizzati:

  • compiti
    • PRIMARIE - TaskID
  • utenti
    • PRIMARIE - UserID
  • tasks_assigned_users
    • PRIMARIA - (TaskID, UserID)
    • indice aggiuntivo UNIQUE - (UserID, TaskID)

EDIT: Inoltre, this page dice che le tabelle derivate vengono eseguiti/materializzato si unisce prima verifica . Perché non riutilizzare i tasti per eseguire il join?

EDIT 2: MySQL Optimizer non vi permetterà di mettere index hints sulle tabelle derivate (presumibilmente perché non ci sono indici sulle tabelle derivate)

EDIT 3: Ecco un post veramente bella di questo : http://venublog.com/2010/03/06/how-to-improve-subqueries-derived-tables-performance/ Si noti che il caso n. 2 è la soluzione che sto cercando, ma sembra che MySQL non lo supporti in questo momento. :(

EDIT 4: Appena trovato this: "A partire da MySQL 5.6.3, l'ottimizzatore gestisce in modo più efficiente subquery nella clausola FROM (vale a dire, le tabelle derivate): ... Durante l'esecuzione di query, l'ottimizzatore è possibile aggiungere un indice a una tabella derivata per accelerare il recupero di righe da esso. "Sembra promettente ...

+0

Puoi anche aggiungere gli indici che stai utilizzando? Presumo che tu abbia un PK sulle attività e un indice non univoco su tasks_assigned_users. – Luis

+0

@Luis - modifica la domanda per te :) – BMiner

+0

Hai ID attività di GROUP BY, che implica che più persone potrebbero lavorare su una determinata attività, il che implica anche un'aggregazione. Vuoi che tutte le persone assegnate a una determinata attività siano elencate in un'unica colonna di ritorno associata all'attività? Oppure, vuoi veramente vedere tutti assegnati a un compito, e quelle attività non assegnate, fallo vuoto. Forse anche spingere qualsiasi attività UNASSIGNED in cima (o in fondo) alla lista ... – DRapp

risposta

4

C'è una soluzione a questo in MySQL Server 5.6 - la versione di anteprima (al momento della stesura di questo) .

http://dev.mysql.com/doc/refman/5.6/en/from-clause-subquery-optimization.html

Anche se, non sono sicuro se il MySQL Optimizer riutilizzare gli indici che già esistono quando si "aggiunge indici alla tabella derivata"

Si consideri la seguente query:

SELECT * FROM t1 JOIN (SELECT * FROM t2) AS derived_t2 ON t1.f1 = derived_t2.f1;

La documentazione dice: "L'ottimizzatore costruisce un indice sulla colonna f1 da derivato_t2 se così facendo consentirebbe l'uso dell'accesso ref per il piano di esecuzione a costi più bassi."

OK, è grandioso, ma l'ottimizzatore riutilizza gli indici da t2? In altre parole, cosa accadrebbe se esistesse un indice per t2.f1? Questo indice viene riutilizzato o l'ottimizzatore ricrea questo indice per la tabella derivata? Chissà?

MODIFICA: La soluzione migliore fino a MySQL 5.6 consiste nel creare una tabella temporanea, creare un indice su tale tabella e quindi eseguire la query SELECT sulla tabella temporanea.

+1

la stessa stupida situazione su MariaDB 10 (anni dopo): anche se group by in derivato accelera la query (ridicola selezionare v1 da t group per v1) a causa dell'ordinamento esplicito, il miglior risultato è se crei tutte le tabelle derivate prima la query principale e aggiungere esplicitamente gli indici necessari. ottimizzatore sux – Tertium

1

Ho paura, è not possible. È necessario creare una tabella temporanea o una vista per utilizzare un indice

+0

Quel post è del 2006. Non ci sono stati cambiamenti da allora? – BMiner

+0

Io non la penso così, per due ragioni. 1) È MySQL. 2) Ci sono altri post su questo problema dal 2010 ad esempio (http://planet.mysql.com/entry/?id=23769). A proposito, potresti usare una vista? – AndreKR

+0

Non so ... ho letto che le viste hanno problemi simili, ma ci proverò adesso ... – BMiner

2

Il problema che vedo è che eseguendo una sottoquery non esiste una tabella indicizzata sottostante. Se si hanno una performance farei il raggruppamento alla fine, qualcosa di simile:

SELECT T.TaskID, T.TaskName, GROUP_CONCAT(U.FirstName, ' ', U.LastName SEPARATOR ', ') AS AssignedUsers 
FROM `tasks` T 
    LEFT OUTER JOIN `tasks_assigned_users` TAU ON (T.TaskID=TAU.TaskID) 
    INNER JOIN `users` U ON (TAU.UserID=U.UserID) 
GROUP BY T.TaskID, T.TaskName 
+0

Questo funziona ... ma poiché è lo stesso identico set di risultati, non so perché MySQL non può fare questa ottimizzazione per me. Inoltre, la mia query * actual * ha circa 20 colonne; Dovrei dire a MySQL di raggrupparli tutti? Io * davvero * voglio solo raggruppare TaskID, non TaskID e TaskName ... MySQL fa un lavoro extra quando si raggruppa per entrambe le colonne. Sai cosa intendo? – BMiner

+0

Prova a raccontare e a non dire; se sono necessari, è un errore non dichiararli così ti renderai conto facilmente (non so se sono necessari).Non so perché non possa fare l'ottimizzazione; la mia ipotesi è che le sottoquery siano una scatola nera per la query esterna, ma non lo so. Normalmente evito le subquery a causa di problemi di prestazioni come questo. – Luis

+0

MySQL è abbastanza intelligente da sapere che GROUP BY T.TaskID, T.TaskName è uguale a GROUP BY T.TaskID perché T.TaskID è il tasto PRIMARY? – BMiner

Problemi correlati