sql >> Database >  >> RDS >> Mysql

Zeer specifieke MySQL-query die ik wil verbeteren

Als je een index hebt met created als de leidende kolom, kan MySQL mogelijk een omgekeerde scan uitvoeren. Als je een periode van 24 uur hebt die geen evenementen heeft, zou je een rij kunnen retourneren die NIET uit die periode komt. Om er zeker van te zijn dat u een rij krijgt in die periode, moet u echt een ondergrens toevoegen aan de created kolom ook, zoiets als dit:

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

Ik denk dat de belangrijkste sleutel tot prestaties hier een index is met created als de leidende kolom, samen met alle (of de meeste) andere kolommen waarnaar wordt verwezen in de WHERE-component, en zorg ervoor dat de index wordt gebruikt door uw zoekopdracht.

Als je een ander tijdsinterval nodig hebt, tot op de seconde nauwkeurig, kan deze benadering gemakkelijk worden veralgemeend.

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

Uit uw code lijkt het alsof de perioden van 24 uur op een willekeurige tijd zijn begrensd... als de tijdfunctie b.v. 1341580800 ('2012-07-06 13:20'), dan zijn je tien menstruaties allemaal van 13:20 op een bepaalde dag tot 13:20 de volgende dag.

(OPMERKING:zorg ervoor dat als uw parameter een unix timestamp integer is, dit correct wordt geïnterpreteerd door de database.)

Het kan efficiënter zijn om de tien rijen in één query op te halen. Als er een garantie is dat 'timestamp' uniek is, dan is het mogelijk om zo'n query te maken, maar de querytekst zal aanzienlijk complexer zijn dan wat je nu hebt. We zouden kunnen knoeien met het krijgen van MAX(timestamp_) binnen elke periode, en dan weer meedoen om de rij te krijgen... maar dat wordt echt rommelig.

Als ik zou proberen om alle tien rijen te trekken, zou ik waarschijnlijk proberen met een UNION ALL te gaan benadering, niet erg mooi, maar het zou op zijn minst kunnen worden afgestemd.

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

Nogmaals, dit kan worden veralgemeend, om in een aantal seconden als argument door te gaan. Vervang HOUR door SECOND en vervang de '24' door een bindparameter met een aantal seconden.

Het is nogal langdradig, maar het zou goed moeten werken.

Een andere erg rommelige en gecompliceerde manier om dit terug te krijgen in een enkele resultatenset, is door een inline-weergave te gebruiken om het eindtijdstempel voor de tien perioden te krijgen, ongeveer als volgt:

     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

En schuif dat dan aan tafel ...

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

En trek MAX (gemaakt) terug voor elke periode GROUP BY p.period_end, wikkel dat in een inline-weergave.

En voeg dat dan weer toe aan je tafel om elke rij te krijgen.

Maar dat is echt heel rommelig, moeilijk te begrijpen en waarschijnlijk niet sneller (of efficiënter) dan wat je al doet. De grootste verbetering die u kunt aanbrengen, is de tijd die nodig is om negen van uw zoekopdrachten uit te voeren.



  1. SQL Self-join-query? Hoe krijg ik categorieën subcategorieën?

  2. Hoe te controleren op duplicaten in mysql-tabel over meerdere kolommen

  3. [BIJGEWERKT 2020-01-23] Microsoft Office 365 Build 1912 Breekt de identiteit van ODBC Linked Tables

  4. MYSQL Id uit een andere tabel invoegen