Inilah salah satu pendekatannya.
Mulailah dengan mengurutkan baris status berdasarkan stempel waktu (tampilan sebaris alias sebagai s
). Kemudian gunakan variabel pengguna MySQL untuk menyimpan nilai dari baris sebelumnya, saat Anda memproses setiap baris.
Apa yang sebenarnya kami cari adalah status 'naik' yang segera mengikuti urutan status 'turun'. Dan ketika kami menemukan baris dengan status 'naik', yang sebenarnya kami butuhkan adalah stempel waktu paling awal dari rangkaian status 'turun' sebelumnya.
Jadi, sesuatu seperti ini akan berhasil:
SELECT d.start_down
, d.ended_down
FROM (SELECT @i := @i + 1 AS i
, @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
, @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
, @status := s.status
FROM (SELECT t.time
, t.status
FROM mydata t
WHERE t.status IN ('up','down')
ORDER BY t.time ASC, t.status ASC
) s
JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i
) d
WHERE d.start_down IS NOT NULL
AND d.ended_down IS NOT NULL
Ini berfungsi untuk kumpulan data tertentu yang Anda tampilkan.
Apa yang tidak ditangani (apa yang tidak dikembalikan) adalah periode 'turun' yang belum berakhir, yaitu urutan status 'turun' tanpa status 'naik'.
Untuk menghindari operasi pengurutan file untuk mengembalikan baris secara berurutan, Anda akan menginginkan indeks penutup pada (time,status)
. Kueri ini akan menghasilkan tabel sementara (MyISAM) untuk mewujudkan tampilan inline alias sebagai d
.
CATATAN: Untuk memahami apa yang dilakukan kueri ini, lepaskan kueri terluar itu, dan jalankan kueri hanya untuk tampilan sebaris alias sebagai d
(Anda dapat menambahkan s.time
ke daftar pilihan.)
Kueri ini mendapatkan setiap baris dengan status 'naik' atau 'turun'. "Trik"nya adalah menetapkan waktu "mulai" dan "akhir" (menandai periode turun) hanya pada baris yang mengakhiri periode 'turun'. (Artinya, baris pertama dengan status 'naik' mengikuti baris dengan status 'turun'.) Di sinilah pekerjaan sebenarnya dilakukan, kueri terluar hanya menyaring semua baris "ekstra" di hasil ini (yang kita tidak perlu.)
SELECT @i := @i + 1 AS i
, @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
, @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
, @status := s.status
, s.time
FROM (SELECT t.time
, t.status
FROM mydata t
WHERE t.status IN ('up','down')
ORDER BY t.time ASC, t.status ASC
) s
JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i
Tujuan tampilan sebaris alias sebagai s
adalah untuk mendapatkan baris yang diurutkan berdasarkan nilai stempel waktu, sehingga kami dapat memprosesnya secara berurutan. Tampilan inline alias sebagai i
hanya ada di sana sehingga kami dapat menginisialisasi beberapa variabel pengguna di awal kueri.
Jika kami menjalankan Oracle atau SQL Server, kami dapat menggunakan "fungsi analitik" atau "fungsi peringkat" (sesuai namanya masing-masing.) MySQL tidak menyediakan hal seperti itu, jadi kami harus "menggulung sendiri ".