Mysql
 sql >> Database >  >> RDS >> Mysql

Query MySQL molto specifica che voglio migliorare

Se hai un indice con created come colonna principale, MySQL potrebbe essere in grado di eseguire una scansione inversa. Se hai un periodo di 24 ore senza eventi, potresti restituire una riga che NON appartiene a quel periodo. Per assicurarti di ottenere una riga in quel periodo, dovresti davvero includere un limite inferiore nel created anche colonna, qualcosa del genere:

SELECT * FROM `eventos`
 WHERE ... 
   AND `created` <  FROM_UNIXTIME( {$timestamp} )
   AND `created` >= DATE_ADD(FROM_UNIXTIME( {$timestamp} ),INTERVAL -24 HOUR)
 ORDER BY `created` DESC
 LIMIT 1

Penso che la chiave per le prestazioni qui sia un indice con created come colonna iniziale, insieme a tutte (o alla maggior parte) delle altre colonne a cui si fa riferimento nella clausola WHERE e assicurandosi che l'indice venga utilizzato dalla query.

Se hai bisogno di un intervallo di tempo diverso, fino al secondo, questo approccio potrebbe essere facilmente generalizzato.

SELECT * FROM `eventos`
 WHERE ... 
   AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL  0*{$nsecs} SECOND)
   AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*{$nsecs} SECOND)
 ORDER BY `created` DESC
 LIMIT 1

Dal tuo codice, sembra che i periodi di 24 ore siano delimitati in un momento arbitrario ... se la funzione del tempo restituisce ad es. 1341580800 ('2012-07-06 13:20'), quindi i tuoi dieci periodi sarebbero tutti dalle 13:20 di un determinato giorno alle 13:20 del giorno successivo.

(NOTA:assicurati che se il tuo parametro è un numero intero di timestamp unix, questo viene interpretato correttamente dal database.)

Potrebbe essere più efficiente estrarre le dieci righe in una singola query. Se c'è la garanzia che "timestamp" sia univoco, è possibile creare una query del genere, ma il testo della query sarà notevolmente più complesso di quello che hai ora. Potremmo pasticciare ottenendo MAX(timestamp_) all'interno di ogni periodo, e poi unirlo di nuovo per ottenere la riga... ma sarà davvero complicato.

Se dovessi provare a estrarre tutte e dieci le righe, probabilmente proverei a utilizzare un UNION ALL approccio, non molto carino, ma almeno potrebbe essere sintonizzato.

SELECT p0.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL  0*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p0 
 UNION ALL           
SELECT p1.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -2*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p1 
 UNION ALL           
SELECT p2.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -2*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -3*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p2 
 UNION ALL           
SELECT p3.*
  FROM ...

Anche in questo caso, questo potrebbe essere generalizzato, per passare in un certo numero di secondi come argomento. Sostituisci ORA con SECONDO e sostituisci "24" con un parametro di associazione che ha un numero di secondi.

È piuttosto prolisso, ma dovrebbe funzionare bene.

Un altro modo davvero complicato e complicato per ottenere questo risultato in un unico set di risultati sarebbe utilizzare una vista in linea per ottenere il timestamp finale per i dieci periodi, qualcosa del genere:

     SELECT p.period_end
       FROM (SELECT DATE_ADD(t.t_,INTERVAL -1 * i.i_* {$nsecs} SECOND) AS period_end
               FROM (SELECT FROM_UNIXTIME( {$timestamp} ) AS t_) t
               JOIN (SELECT 0 AS i_
                     UNION ALL SELECT 1
                     UNION ALL SELECT 2
                     UNION ALL SELECT 3
                     UNION ALL SELECT 4
                     UNION ALL SELECT 5
                     UNION ALL SELECT 6
                     UNION ALL SELECT 7
                     UNION ALL SELECT 8
                     UNION ALL SELECT 9
                    ) i
            ) p

E poi uniscilo al tuo tavolo ...

  ON `created` < p.period_end
 AND `created` >= DATE_ADD(p.period_end,INTERVAL -1 * {$nsecs} SECOND)

E tira indietro MAX (creato) per ogni periodo GROUP BY p.period_end, avvolgilo in una vista in linea.

E poi uniscilo di nuovo alla tua tabella per ottenere ogni riga.

Ma questo è davvero, davvero disordinato, difficile da capire e probabilmente non sarà più veloce (o più efficiente) di quello che stai già facendo. Il massimo miglioramento che potresti apportare è il tempo necessario per eseguire 9 delle tue query.