Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Permintaan MySQL yang sangat spesifik yang ingin saya tingkatkan

Jika Anda memiliki indeks dengan created sebagai kolom terdepan, MySQL mungkin bisa melakukan reverse scan. Jika Anda memiliki periode 24 jam yang tidak memiliki acara apa pun, Anda dapat mengembalikan baris yang BUKAN dari periode itu. Untuk memastikan Anda mendapatkan baris dalam periode itu, Anda benar-benar perlu menyertakan batas bawah pada created kolom juga, kira-kira seperti ini:

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

Saya pikir kunci besar untuk kinerja di sini adalah indeks dengan created sebagai kolom utama, bersama dengan semua (atau sebagian besar) kolom lain yang dirujuk dalam klausa WHERE, dan memastikan bahwa indeks digunakan oleh kueri Anda.

Jika Anda membutuhkan interval waktu yang berbeda, hingga detik, pendekatan ini dapat dengan mudah digeneralisasikan.

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

Dari kode Anda, sepertinya periode 24 jam dibatasi pada waktu yang berubah-ubah... jika fungsi waktu kembali mis. 1341580800 ('2012-07-06 13:20'), maka sepuluh periode Anda semuanya adalah dari 13:20 pada hari tertentu hingga 13:20 pada hari berikutnya.

(CATATAN:pastikan bahwa jika parameter Anda adalah bilangan bulat stempel waktu unix, ini ditafsirkan dengan benar oleh database.)

Mungkin lebih efisien untuk menarik sepuluh baris dalam satu kueri. Jika ada jaminan bahwa 'stempel waktu' unik, maka kueri semacam itu dapat dibuat, tetapi teks kueri akan jauh lebih kompleks daripada yang Anda miliki sekarang. Kita bisa mengacaukan dengan mendapatkan MAX(timestamp_) dalam setiap periode, dan kemudian menggabungkannya kembali untuk mendapatkan baris... tapi itu akan sangat berantakan.

Jika saya akan mencoba menarik semua sepuluh baris, saya mungkin akan mencoba menggunakan UNION ALL pendekatan, tidak terlalu cantik, tapi setidaknya bisa disetel.

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

Sekali lagi, ini bisa digeneralisasi, untuk dilewatkan dalam beberapa detik sebagai argumen. Ganti HOUR dengan SECOND, dan ganti '24' dengan parameter bind yang memiliki jumlah detik.

Agak panjang lebar, tapi seharusnya berjalan dengan baik.

Cara lain yang benar-benar berantakan dan rumit untuk mengembalikan ini dalam satu set hasil adalah dengan menggunakan tampilan sebaris untuk mendapatkan stempel waktu akhir selama sepuluh periode, seperti ini:

     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

Dan kemudian gabungkan itu ke meja Anda ...

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

Dan tarik kembali MAX(dibuat) untuk setiap periode GROUP BY p.period_end, bungkus itu dalam tampilan sebaris.

Lalu gabungkan kembali ke meja Anda untuk mendapatkan setiap baris.

Tapi itu benar-benar berantakan, sulit dimengerti, dan tidak mungkin lebih cepat (atau lebih efisien) daripada apa yang sudah Anda lakukan. Peningkatan terbesar yang dapat Anda lakukan adalah waktu yang diperlukan untuk menjalankan 9 kueri Anda.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. pilih opsi untuk memperbarui opsi pilih kedua berdasarkan dropdown yang diisi mysql

  2. Pertanyaan tentang tipe di MySQL

  3. Ekstrak kunci utama dari MySQL di PHP

  4. Hibernate - batasan kolom unik diabaikan

  5. Filter menurut COUNT(*)?