sql >> Database teknologi >  >> RDS >> Mysql

Meget specifik MySQL-forespørgsel, jeg ønsker at forbedre

Hvis du har et indeks med created som den førende kolonne kan MySQL muligvis lave en omvendt scanning. Hvis du har en 24 timers periode, der ikke har nogen begivenheder, kan du returnere en række, der IKKE er fra den periode. For at sikre, at du får en række i den periode, skal du virkelig inkludere en nedre grænse for den created kolonne også, noget som dette:

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

Jeg tror, ​​at den store nøgle til ydeevne her er et indeks med created som den ledende kolonne sammen med alle (eller de fleste) af de andre kolonner, der henvises til i WHERE-sætningen, og sørg for, at indekset bruges af din forespørgsel.

Hvis du har brug for et andet tidsinterval, ned til det andet, kan denne tilgang let generaliseres.

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

Ud fra din kode ser det ud som om 24-timers perioderne er afgrænset til et vilkårligt tidspunkt... hvis tidsfunktionen returnerer f.eks. 1341580800 ('2012-07-06 13:20'), så vil dine ti menstruationer alle være fra 13:20 på en given dag til 13:20 den følgende dag.

(BEMÆRK:vær sikker på, at hvis din parameter er et unix-tidsstempel heltal, at dette bliver fortolket korrekt af databasen.)

Det kan være mere effektivt at trække de ti rækker i en enkelt forespørgsel. Hvis der er garanti for, at 'tidsstempel' er unikt, så er det muligt at lave sådan en forespørgsel, men forespørgselsteksten vil være betydeligt mere kompleks, end den du har nu. Vi kunne rode med at få MAX(tidsstempel_) inden for hver periode, og så slå det sammen for at få rækken... men det bliver virkelig rodet.

Hvis jeg skulle prøve at trække alle ti rækker, ville jeg nok prøve at gå med en UNION ALL tilgang, ikke særlig smuk, men den kunne i det mindste indstilles.

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

Igen, dette kunne generaliseres til at passere på et antal sekunder som et argument. Erstat HOUR med SECOND, og ​​erstat '24' med en bindeparameter, der har et antal sekunder.

Det er temmelig langhåret, men det burde køre okay.

En anden virkelig rodet og kompliceret måde at få dette tilbage i et enkelt resultatsæt ville være at bruge en indlejret visning til at få sluttidsstemplet for de ti perioder, noget som dette:

     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

Og sæt det så til dit bord ...

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

Og træk MAX(oprettet) tilbage for hver periode GROUP BY p.period_end, indpak det i en indlejret visning.

Og sæt det derefter tilbage til dit bord for at få hver række.

Men det er virkelig, virkelig rodet, svært at forstå og vil sandsynligvis ikke være hurtigere (eller mere effektivt) end det, du allerede gør. Den største forbedring, du kan foretage, er den tid, det tager at køre 9 af dine forespørgsler.



  1. 3 måder at formatere et tal som en procentdel i PostgreSQL

  2. MySQL tæller komplekse forespørgselsresultater?

  3. Understøtter MS-SQL tabeller i hukommelsen?

  4. inden for gruppe sorterer i mysql