Mysql
 sql >> Base de données >  >> RDS >> Mysql

Requête MySQL très spécifique que je souhaite améliorer

Si vous avez un index avec created en tant que colonne principale, MySQL peut être en mesure d'effectuer une analyse inversée. Si vous avez une période de 24 heures qui n'a pas d'événements, vous pourriez renvoyer une ligne qui n'est PAS de cette période. Pour vous assurer que vous obtenez une ligne pendant cette période, vous devez vraiment inclure une limite inférieure sur le created colonne également, quelque chose comme ceci :

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

Je pense que la grande clé de la performance ici est un index avec created comme colonne principale, avec toutes (ou la plupart) des autres colonnes référencées dans la clause WHERE, et en s'assurant que l'index est utilisé par votre requête.

Si vous avez besoin d'un intervalle de temps différent, jusqu'à la seconde près, cette approche peut être facilement généralisée.

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

D'après votre code, il semble que les périodes de 24 heures soient limitées à une heure arbitraire... si la fonction time renvoie par ex. 1341580800 ('2012-07-06 13:20'), alors vos dix règles seraient toutes de 13h20 un jour donné à 13h20 le lendemain.

(REMARQUE :assurez-vous que si votre paramètre est un entier d'horodatage Unix, il est correctement interprété par la base de données.)

Il peut être plus efficace d'extraire les dix lignes dans une seule requête. S'il existe une garantie que "l'horodatage" est unique, il est alors possible de créer une telle requête, mais le texte de la requête sera considérablement plus complexe que ce que vous avez actuellement. Nous pourrions nous tromper en obtenant MAX(timestamp_) dans chaque période, puis en le rejoignant pour obtenir la ligne... mais ça va être vraiment compliqué.

Si j'essayais de tirer les dix lignes, j'essaierais probablement d'utiliser un UNION ALL approche, pas très jolie, mais elle pourrait au moins être réglée.

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 ...

Encore une fois, cela pourrait être généralisé, passer en nombre de secondes comme argument. Remplacez HOUR par SECOND et remplacez '24' par un paramètre de liaison qui a un nombre de secondes.

C'est assez long, mais ça devrait bien fonctionner.

Une autre façon vraiment désordonnée et compliquée de récupérer cela dans un seul ensemble de résultats serait d'utiliser une vue en ligne pour obtenir l'horodatage de fin pour les dix périodes, quelque chose comme ceci :

     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

Et ensuite joignez ça à votre table...

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

Et retirez MAX (créé) pour chaque période GROUP BY p.period_end, encapsulez cela dans une vue en ligne.

Ensuite, joignez-le à votre table pour obtenir chaque ligne.

Mais c'est vraiment, vraiment désordonné, difficile à comprendre et peu susceptible d'être plus rapide (ou plus efficace) que ce que vous faites déjà. L'amélioration la plus importante que vous pourriez apporter est le temps qu'il faut pour exécuter 9 de vos requêtes.