Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Consulta MySQL muito específica que quero melhorar


Se você tiver um índice com created como a coluna principal, o MySQL pode fazer uma varredura reversa. Se você tiver um período de 24 horas sem nenhum evento, poderá estar retornando uma linha que NÃO seja desse período. Para ter certeza de que está recebendo uma linha nesse período, você realmente precisa incluir um limite inferior no created coluna também, algo assim:
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

Acho que a grande chave para o desempenho aqui é um índice com created como a coluna inicial, juntamente com todas (ou a maioria) das outras colunas referenciadas na cláusula WHERE, e certificando-se de que o índice seja usado por sua consulta.

Se você precisar de um intervalo de tempo diferente, até o segundo, essa abordagem pode ser facilmente generalizada.
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

Do seu código, parece que os períodos de 24 horas são limitados em um horário arbitrário... se a função de tempo retornar, por exemplo 1341580800 ('2012-07-06 13:20'), então seus dez períodos seriam todos das 13:20 de um determinado dia às 13:20 do dia seguinte.

(OBSERVAÇÃO:certifique-se de que, se seu parâmetro for um inteiro de timestamp unix, isso está sendo interpretado corretamente pelo banco de dados.)

Pode ser mais eficiente extrair as dez linhas em uma única consulta. Se houver uma garantia de que 'timestamp' é único, então é possível criar tal consulta, mas o texto da consulta será consideravelmente mais complexo do que o que você tem agora. Poderíamos mexer em obter MAX(timestamp_) dentro de cada período, e depois juntar isso de volta para obter a linha... mas isso vai ser muito confuso.

Se eu fosse tentar puxar todas as dez linhas, provavelmente tentaria usar um UNION ALL abordagem, não muito bonita, mas pelo menos poderia ser afinada.
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 ...

Novamente, isso pode ser generalizado, para passar um número de segundos como um argumento. Substitua HOUR por SECOND e substitua '24' por um parâmetro de ligação que tenha um número de segundos.

É bastante prolixo, mas deve funcionar bem.

Outra maneira realmente confusa e complicada de recuperar isso em um único conjunto de resultados seria usar uma visualização em linha para obter o carimbo de data/hora final dos dez períodos, algo assim:
     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 então junte isso à sua mesa...
  ON `created` < p.period_end
 AND `created` >= DATE_ADD(p.period_end,INTERVAL -1 * {$nsecs} SECOND)

E puxe MAX(created) para cada período GROUP BY p.period_end, envolva isso em uma visualização inline.

E então junte isso de volta à sua tabela para obter cada linha.

Mas isso é muito, muito confuso, difícil de entender e provavelmente não será mais rápido (ou mais eficiente) do que o que você já está fazendo. A maior melhoria que você pode fazer é o tempo que leva para executar 9 de suas consultas.