Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Bardzo konkretne zapytanie MySQL, które chcę poprawić

Jeśli masz indeks z created jako wiodąca kolumna MySQL może być w stanie wykonać skanowanie wsteczne. Jeśli masz okres 24 godzin, w którym nie ma żadnych wydarzeń, możesz zwracać wiersz, który NIE pochodzi z tego okresu. Aby upewnić się, że otrzymujesz wiersz w tym okresie, naprawdę musisz uwzględnić dolną granicę dla created również kolumnę, coś takiego:

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

Myślę, że kluczem do wydajności jest tutaj indeks z created jako kolumnę wiodącą, wraz ze wszystkimi (lub większością) innych kolumn, do których odwołuje się klauzula WHERE, i upewniając się, że indeks jest używany przez zapytanie.

Jeśli potrzebujesz innego przedziału czasu, z dokładnością do sekundy, to podejście można łatwo uogólnić.

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

Z twojego kodu wygląda na to, że okresy 24-godzinne są ograniczone w dowolnym czasie... jeśli funkcja czasu zwraca np. 1341580800 („2012-07-06 13:20”), wtedy wszystkie twoje dziesięć okresów będzie trwało od 13:20 danego dnia do 13:20 następnego dnia.

(UWAGA:upewnij się, że jeśli twój parametr jest uniksową liczbą całkowitą ze znacznikiem czasu, to jest to poprawnie interpretowane przez bazę danych.)

Bardziej wydajne może być pobranie dziesięciu wierszy w jednym zapytaniu. Jeśli istnieje gwarancja, że ​​„sygnatura czasowa” jest unikatowa, można stworzyć takie zapytanie, ale tekst zapytania będzie znacznie bardziej złożony niż ten, który masz teraz. Moglibyśmy namieszać z uzyskaniem MAX(timestamp_) w każdym okresie, a następnie dołączeniem do tego z powrotem, aby uzyskać wiersz… ale to będzie naprawdę nieporządne.

Gdybym miał spróbować wyciągnąć wszystkie dziesięć wierszy, prawdopodobnie spróbowałbym użyć UNION ALL podejście, niezbyt ładne, ale przynajmniej można je dostroić.

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

Znowu można to uogólnić, aby przekazać jako argument w ciągu kilku sekund. Zamień GODZINĘ na SECOND i zastąp „24” parametrem wiązania, który ma liczbę sekund.

Jest dość długi, ale powinien działać poprawnie.

Innym naprawdę niechlujnym i skomplikowanym sposobem odzyskania tego w jednym zestawie wyników byłoby użycie widoku wbudowanego w celu uzyskania znacznika czasu zakończenia dla dziesięciu okresów, coś takiego:

     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

A potem dołącz do swojego stołu...

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

I wycofaj MAX(utworzone) dla każdego okresu GROUP BY p.period_end, zapakuj to w widok wbudowany.

A następnie dołącz go z powrotem do swojego stołu, aby uzyskać każdy wiersz.

Ale to jest naprawdę, bardzo niechlujne, trudne do zrozumienia i prawdopodobnie nie będzie szybsze (lub bardziej wydajne) niż to, co już robisz. Największą poprawą, jaką możesz wprowadzić, jest czas potrzebny na uruchomienie 9 zapytań.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Indeksujesz drzewo k-d?

  2. Zapytanie wybierające MySQL ze zmienną nazwą kolumny

  3. Facebook user_id :big_int, int czy string?

  4. Znajdź grupę rekordów, które pasują do wielu wartości

  5. Czy ktoś ma spory dowód na to, że CHAR jest szybszy niż VARCHAR?