sql >> Databasteknik >  >> RDS >> Mysql

Mycket specifik MySQL-fråga jag vill förbättra

Om du har ett index med created som den ledande kolumnen kan MySQL kanske göra en omvänd skanning. Om du har en 24-timmarsperiod som inte har några händelser, kan du returnera en rad som INTE är från den perioden. För att vara säker på att du får en rad under den perioden skulle du verkligen behöva inkludera en nedre gräns för den created kolumn också, ungefär så här:

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

Jag tror att den stora nyckeln till prestanda här är ett index med created som den inledande kolumnen, tillsammans med alla (eller de flesta) andra kolumner som refereras till i WHERE-satsen, och se till att indexet används av din fråga.

Om du behöver ett annat tidsintervall, ner till det andra, kan detta tillvägagångssätt lätt generaliseras.

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

Från din kod ser det ut som att 24-timmarsperioderna är avgränsade till en godtycklig tidpunkt... om tidsfunktionen returnerar t.ex. 1341580800 ('2012-07-06 13:20'), då skulle dina tio mens alla vara från 13:20 en viss dag till 13:20 följande dag.

(OBS:se till att om din parameter är ett unix-tidsstämpel heltal, att detta tolkas korrekt av databasen.)

Det kan vara mer effektivt att dra de tio raderna i en enda fråga. Om det finns en garanti för att "tidsstämpel" är unik, så är det möjligt att skapa en sådan fråga, men frågetexten kommer att vara betydligt mer komplex än vad du har nu. Vi skulle kunna bråka med att få MAX(tidsstämpel_) inom varje period och sedan slå tillbaka det för att få raden... men det kommer att bli riktigt rörigt.

Om jag skulle försöka dra alla tio raderna skulle jag förmodligen försöka gå med en UNION ALL tillvägagångssätt, inte särskilt vackert, men det kunde åtminstone ställas in.

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

Återigen, detta skulle kunna generaliseras, att passera på ett antal sekunder som ett argument. Ersätt HOUR med SECOND och ersätt "24" med en bindningsparameter som har ett antal sekunder.

Det är ganska långdraget, men det borde gå okej.

Ett annat riktigt rörigt och komplicerat sätt att få tillbaka detta i en enda resultatuppsättning skulle vara att använda en inlinevy för att få sluttidsstämpeln för de tio perioderna, ungefär så här:

     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

Och lägg sedan det till ditt bord ...

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

Och dra tillbaka MAX(skapat) för varje period GROUP BY p.period_end, slå in det i en inlinevy.

Och sätt sedan tillbaka det till ditt bord för att få varje rad.

Men det är verkligen, riktigt rörigt, svårt att förstå och sannolikt inte vara snabbare (eller effektivare) än vad du redan gör. Den största förbättringen du kan göra är den tid det tar att köra 9 av dina frågor.



  1. RMAN Lista backup-kommandon

  2. MySQL med Sum och Case

  3. Hur man beräknar totala resttimmar mellan stad x och y och vice versa

  4. mysql uppdateringstabell ajax efter klass