2010-08-11 8 views
17

So che posso cambiare il modo in cui MySQL esegue una query utilizzando la parola chiave FORCE INDEX (abc). Ma c'è un modo per cambiare l'ordine di esecuzione?Esiste un modo per forzare l'ordine di esecuzione MySQL?

La mia domanda si presenta in questo modo:

SELECT c.* 
FROM table1 a 
INNER JOIN table2 b ON a.id = b.table1_id 
INNER JOIN table3 c ON b.itemid = c.itemid 
WHERE a.itemtype = 1 
    AND a.busy = 1 
    AND b.something = 0 
    AND b.acolumn = 2 
    AND c.itemid = 123456 

ho una chiave per ogni relazione/vincolo che uso. Se corro spiegare su questa affermazione, vedo che mysql inizia a interrogare c prima.

id select_type table type 
1  SIMPLE   c  ref 
2  SIMPLE   b  ref 
3  SIMPLE   a  eq_ref 

Tuttavia, so che l'interrogazione in ordine a -> b -> c sarebbe più veloce (ho dimostrato che) C'è un modo per dire a MySQL di utilizzare un ordine specifico?

Aggiornamento: È così che so che a -> b -> c è più veloce.

La query precedente richiede 1,9 secondi per essere completata e restituisce 7 righe. Se cambio la query per

SELECT c.* 
FROM table1 a 
INNER JOIN table2 b ON a.id = b.table1_id 
INNER JOIN table3 c ON b.itemid = c.itemid 
WHERE a.itemtype = 1 
    AND a.busy = 1 
    AND b.something = 0 
    AND b.acolumn = 2 
HAVING c.itemid = 123456 

della query di 0,01 secondi (Senza usare avente ottengo 10.000 righe). Tuttavia questa non è una soluzione elegante perché questa query è un esempio semplificato. Nel mondo reale mi sono unito da c ad altri tavoli. Dal momento che HAVING è un filtro che viene eseguito sull'intero risultato, significherebbe che potrei estrarre alcuni record più dal db che da quello necessario.

Edit2: Solo alcune informazioni:

  • La parte variabile in questa query è c.itemid. Tutto il resto sono valori fissi che non cambiano.
  • indici sono installati bene e MySQL sceglie quelle giuste per me
    • tra A e B c'è un 1: n relazione (indice primario è utilizzato)
    • tra B e C non v'è una relazione molti a molti relazione (indice IDX_ITEMID viene utilizzato)

il punto è che mysql dovrebbe iniziare interrogazione della tabella ae lavorare è modo giù a c e non viceversa. Qualsiasi cambiamento per ottenere quello.

Soluzione: Non è esattamente quello che volevo, ma questo sembra funzionare:

SELECT c.* 
FROM table1 a 
INNER JOIN table2 b ON a.id = b.table1_id 
INNER JOIN table3 c ON b.itemid = c.itemid 
WHERE a.itemtype = 1 
    AND a.busy = 1 
    AND b.something = 0 
    AND b.acolumn = 2 
    AND c.itemid = 123456 
    AND f.id IN (
     SELECT DISTINCT table2.id FROM table1 
     INNER JOIN table2 ON table1.id = table2.table1_id 
     WHERE table1.itemtype = 1 AND table1.busy = 1) 
+1

Come hai dimostrato che 'a -> b -> c' sarebbe più veloce? Inoltre, puoi mostrare i tuoi indici? – Unreason

+0

Per quanto riguarda i test che sono più veloci, può essere difficile verificarlo senza avere una grande quantità di dati pre-caricati e simulando un carico normale. Spesso è bene lasciare che MySQL faccia la propria ottimizzazione, che diventa sempre più accurata più dati si hanno (e nel tempo, o si esegue ANALYZE TABLE una o due volte), e si forzano solo gli indici se si incontrano problemi. Tali problemi esistono però; Ho sicuramente avuto situazioni in cui il piano di query di MySQL era patologicamente negativo ma dopo aver forzato un certo indice e quindi unirmi all'ordine, ha funzionato in modo efficiente. – thomasrutter

+0

@thomasrutter: Se l'ottimizzazione di MySQL risulta in una query 2-7 sec (dipende dal carico/quantità di dati) non ho altra scelta che usare le mie ottimizzazioni :) AS Hammerite ha suggerito: STRAIGHT_JOIN fa ciò che voglio e 1 ha fissato la query migliaia di volte. –

risposta

28

Forse è necessario utilizzare STRAIGHT_JOIN.

http://dev.mysql.com/doc/refman/5.0/en/join.html

STRAIGHT_JOIN è simile a JOIN, salvo che la tabella di sinistra viene sempre letto prima della tabella di destra. Questo può essere usato per quei (pochi) casi per i quali lo strumento di ottimizzazione dei join mette le tabelle nell'ordine sbagliato.

+2

'STRAIGHT_JOIN è simile a JOIN, tranne che la tabella di sinistra viene sempre letta prima della tabella di destra. Questo può essere usato per quei (pochi) casi per i quali l'ottimizzatore di join mette le tabelle nell'ordine sbagliato. Era esattamente quello che stavo cercando. Ora la mia spiegazione mostra 'a -> b -> c' e non ho bisogno di un SUBSELECT. –

+0

Molto bello. Strano, tuttavia, che 'STRAIGHT_JOIN' possa solo eseguire un join interno e non un join esterno. http://forums.mysql.com/read.php?115,565624,565668#msg-565668 prova a spiegare perché (ultima riga), ma semplicemente non è corretto. L'ottimizzatore può e cambia l'ordine di alcuni 'LEFT JOIN' uguali, come mostrato da' EXPLAIN', e sembra che non ci sia una cura 'STRAIGHT_JOIN' per quelli. – Timo

2

Si può provare a riscrivere in due modi

  • portare un po 'della condizione WHERE in JOIN
  • introdurre sottoquery anche se non sono necessari

Entrambe le cose potrebbero impa ct il pianificatore.

La prima cosa da verificare, tuttavia, sarebbe se il vostro stats fosse aggiornato.

+1

L'aggiunta del risultato a JOIN non ha modificato nulla, ma ho incluso una sottoquery per ridurre la quantità di righe in b. Il piano di esecuzione mostra ancora 'c -> b -> a' ma sembra funzionare (guarda la mia domanda aggiornata). –

+0

L'aggiunta di una condizione al JOIN non migliorerà davvero nulla. Per osservare ciò, prima di eseguire la query con "explain extended", quindi dopo la query aggiungere "show warnings;". Gli avvisi di visualizzazione mostreranno la query come "ottimizzata" dal motore di ottimizzazione. Sposta tutti i requisiti JOIN nella clausola WHERE più vicina ... – Nick

2

È possibile utilizzare FORCE INDEX per forzare l'ordine di esecuzione e l'ho già fatto.

Se ci pensate, di solito c'è solo un ordine per poter interrogare le tabelle per ogni indice che scegliete.

In questo caso, se si desidera che MySQL inizi a interrogare a, assicurarsi che l'indice che si forza su b sia uno che contenga b.table1_id. MySQL sarà in grado di utilizzare quell'indice solo se è già stato interrogato prima a.

+0

+1 Buona risposta. – Unreason

+0

@Unreason, perché? L'altra risposta sopra è la buona risposta. – Pacerier

+1

Poiché in alcuni casi non è possibile modificare i join esistenti, è possibile aggiungere condizioni aggiuntive come l'indice di forza –