Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Sehr spezifische MySQL-Abfrage, die ich verbessern möchte

Wenn Sie einen Index mit created haben als führende Spalte kann MySQL möglicherweise einen Rückwärtsscan durchführen. Wenn Sie einen 24-Stunden-Zeitraum ohne Ereignisse haben, könnten Sie eine Zeile zurückgeben, die NICHT aus diesem Zeitraum stammt. Um sicherzustellen, dass Sie in diesem Zeitraum eine Zeile erhalten, müssten Sie wirklich eine untere Grenze in den created einfügen Spalte, etwa so:

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

Ich denke, der große Schlüssel zur Leistung ist hier ein Index mit created als führende Spalte, zusammen mit allen (oder den meisten) anderen Spalten, auf die in der WHERE-Klausel verwiesen wird, und stellen Sie sicher, dass dieser Index von Ihrer Abfrage verwendet wird.

Wenn Sie ein anderes Zeitintervall bis auf die Sekunde benötigen, könnte dieser Ansatz leicht verallgemeinert werden.

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

Aus Ihrem Code sieht es so aus, als ob die 24-Stunden-Perioden zu einer beliebigen Zeit begrenzt sind ... wenn die Zeitfunktion z. 1341580800 ('2012-07-06 13:20'), dann wären Ihre zehn Perioden alle von 13:20 an einem bestimmten Tag bis 13:20 am folgenden Tag.

(HINWEIS:Stellen Sie sicher, dass, wenn Ihr Parameter eine Unix-Zeitstempel-Ganzzahl ist, diese von der Datenbank richtig interpretiert wird.)

Es ist möglicherweise effizienter, die zehn Zeilen in einer einzigen Abfrage abzurufen. Wenn es eine Garantie dafür gibt, dass „timestamp“ eindeutig ist, dann ist es möglich, eine solche Abfrage zu erstellen, aber der Abfragetext wird erheblich komplexer sein als das, was Sie jetzt haben. Wir könnten damit herumspielen, MAX(timestamp_) innerhalb jeder Periode zu bekommen, und das dann wieder zusammenfügen, um die Zeile zu bekommen... aber das wird wirklich chaotisch.

Wenn ich versuchen würde, alle zehn Zeilen zu ziehen, würde ich es wahrscheinlich mit einem UNION ALL versuchen Ansatz, nicht sehr hübsch, aber zumindest könnte es abgestimmt werden.

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

Auch dies könnte verallgemeinert werden, um eine Anzahl von Sekunden als Argument zu übergeben. Ersetzen Sie HOUR durch SECOND und ersetzen Sie die '24' durch einen Bindungsparameter, der eine Anzahl von Sekunden enthält.

Es ist ziemlich langatmig, aber es sollte gut laufen.

Eine andere wirklich chaotische und komplizierte Möglichkeit, dies in einem einzelnen Ergebnissatz wiederzuerlangen, wäre die Verwendung einer Inline-Ansicht, um den Endzeitstempel für die zehn Perioden zu erhalten, etwa so:

     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

Und dann stellen Sie das an Ihren Tisch ...

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

Und ziehen Sie MAX(created) für jeden Zeitraum GROUP BY p.period_end zurück, packen Sie das in eine Inline-Ansicht.

Und verbinden Sie das dann wieder mit Ihrer Tabelle, um jede Zeile zu erhalten.

Aber das ist wirklich, wirklich chaotisch, schwer zu verstehen und wahrscheinlich nicht schneller (oder effizienter) als das, was Sie bereits tun. Die größte Verbesserung, die Sie vornehmen könnten, ist die Zeit, die zum Ausführen von 9 Ihrer Abfragen benötigt wird.