2009-06-30 25 views
5

Questa query SQL mi fa schifo. Non l'ho scritto, ma è una massiccia causa di problemi sui nostri server. Sono disposto a dividerlo in più query e ad eseguire parte dell'elaborazione tramite PHP (ad esempio, il RAND()).Ottimizza questa query SQL

$sql = "SELECT a.code, a.ad_id, a.position, a.type, a.image, a.url, a.height, a.width 
    FROM " . AD_TABLE ." a, " . USER_GROUP_TABLE . " g 
    WHERE (a.max_views >= a.views OR a.max_views = '0') 
    AND (FIND_IN_SET(" .$forum_id. ", a.show_forums) > 0 OR a.show_all_forums = '1') 
    AND g.user_id = " . $user->data['user_id'] . " 
    AND FIND_IN_SET(g.group_id, a.groups) 
    AND FIND_IN_SET(" . $user->data['user_rank'] . ", a.ranks) 
    AND a.start_time < " . time() . " 
    AND a.end_time > " . time() . " 
    AND (a.clicks <= a.max_clicks OR a.max_clicks = '0') 
    ORDER BY rand()"; 

Yeesh, mi sento icky dopo aver incollato che ...

EDIT: Di seguito i risultati del "spiegare" in una query di esempio nel formato di cui sopra, separati da virgole:

"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra" 
1,"SIMPLE","g","ref","user_id","user_id","3","const",6,"Using temporary; Using filesort" 
1,"SIMPLE","a","ALL","max_views","","","",10,"Using where" 

È

+2

Stai facendo una domanda o stai cercando di ottenere qualche SQL ottimizzato senza pagare per questo? – Mathew

+0

Ti dispiacerebbe fare una SELEZIONA SCENA e pubblicare i risultati? – alexn

+2

Disgust è il motore della correzione. – RedFilter

risposta

6

si hanno tre grandi questioni qui:

  1. FIND_IN_SET.

    Non è sargibile, un indice non può renderlo più veloce. Crea una tabella di relazioni molti-a-molti (o tabelle).

    • a.start_time < GETDATE() AND a.end_time > GETDATE()

    MySQL non è buono nell'ottimizzazione questo. Potete tenervi timespans come caselle di geometria e creare un SPATIAL INDEX su di loro, questo sarà molto più veloce (anche se meno leggibile)

    • ORDER BY RAND()

    Se si sta utilizzando questo per campionare i dati (per esempio, si don 't bisogno di tutte le righe, ma piuttosto un piccolo sottoinsieme casuale), c'è un modo più efficiente per fare questo, descritto in questo articolo nel mio blog:

1

Immagino che il tuo problema sia dovuto alle date.

AND a.start_time < " . time() . " 
AND a.end_time > " . time() . " 

Proverei a inserire indici su questi campi e vedere se questo aiuta. Confrontando le date, il database viene confrontato con ciascuna riga della tabella.

4
  • È non si dispone di un esplicito join in questa query tra il tavolo e un tavolo g: sono legati solo da find_in_set (g.group_id, a.groups)
  • Quanto grande è la vostra impostato in "a.groups", cioè la stringa csv contiene un id di gruppo la maggior parte delle volte?
  • Se il 99% dei casi contiene solo 1 gruppo, quindi creare un ciclo foreach su "a.groups" in php ed eseguire un join reale (o probabilmente potrebbe eliminare del tutto user_group_table) dalla query.
    • Per la minoranza dei casi in cui si dispone di più di 1 appartenenza a un gruppo, la query verrà comunque eseguita correttamente con un join esplicito.

Questo aggiungerà un po 'di codice nel tuo classe PHP/funzione.

+0

Ok, che ne dici di un altro tentativo. Questo potrebbe essere molto semplice. Utilizzare "e a.groups come". "%". g.group_id. "%" Questo valuterà "e" grp1, grp2, grp4 'come'% grp1% '"e otterrai il risultato. – blispr

0

Se si esegue questo molto .. cercare di utilizzare le variabili di bind (invece di concatenazione di stringhe) .. quindi la query non deve essere analizzato ogni singola volta ..

edit: Sorry..didn' Vedi il tag MYSQL. A meno che non sia cambiato di recente, non penso che a MySQL piacciano le dichiarazioni preparate. ORACLE d'altra parte li ADORANO.

Problemi correlati