Saya akan melempar topi saya ke atas ring dengan pendekatan lain:
Sunting: Saya agak terlambat menyadari bahwa fungsi Oracle yang dimaksud mengambil string sebagai argumen kedua, jadi ini tidak sesuai dengan persyaratan. Namun MySQL telah dengan ramah mendefinisikan 0 - 6 sebagai Senin - Minggu, dan bagaimanapun saya memiliki keberatan moral untuk menggunakan string sebagai argumen untuk hal semacam ini. String akan berasal dari input pengguna atau pemetaan lain dalam kode tingkat yang lebih tinggi antara nilai numerik dan string. Mengapa tidak lulus bilangan bulat? :)
CREATE FUNCTION `fnDayOfWeekGetNext`(
p_date DATE,
p_weekday TINYINT(3)
) RETURNS date
BEGIN
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + (ROUND(WEEKDAY(p_date) / (p_weekday + WEEKDAY(p_date) + 1)) * 7) DAY);
END
Untuk memecah bagian yang menentukan INTERVAL
nilai:
Bagian pertama persamaan hanya mendapatkan offset antara hari kerja yang ditentukan dan hari kerja dari tanggal yang ditentukan:
p_weekday - WEEKDAY(p_date)
Ini akan mengembalikan angka positif jika p_weekday
lebih besar dari WEEKDAY(p_date)
dan sebaliknya. Nol akan dikembalikan jika sama.
ROUND()
segmen digunakan untuk menentukan apakah hari yang diminta dalam seminggu (p_weekday
) telah terjadi dalam minggu ini relatif terhadap tanggal (p_date
) ditentukan. Jadi, dengan contoh...
ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..mengembalikan 0
, menunjukkan bahwa hari Minggu (6
) belum terjadi minggu ini, karena 2019-01-25
adalah hari Jumat. Demikian juga...
ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
...mengembalikan 1
karena hari Rabu (2
) telah berlalu. Perhatikan bahwa ini akan mengembalikan 0
jika p_weekday
sama dengan hari kerja p_date
.
Nilai ini (baik 1
atau 0
) kemudian dikalikan dengan konstanta 7
(jumlah hari dalam seminggu).
Oleh karena itu jika p_weekday
telah terjadi dalam minggu ini, itu akan menambahkan 7 ke offset p_weekday - WEEKDAY(p_date)
, karena offset itu akan menjadi angka negatif dan kami ingin tanggal di masa mendatang.
Jika p_weekday
belum terjadi di minggu ini, maka kita bisa menambahkan offset ke tanggal saat ini karena offset akan menjadi angka positif. Oleh karena itu bagian ROUND(...) * 7
sama dengan nol dan, pada dasarnya, diabaikan.
Keinginan saya untuk pendekatan ini adalah untuk mensimulasikan IF()
kondisi secara matematis. Ini akan sama validnya:
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
Dan untuk kepentingan objektivitas, dalam menjalankan 1 juta iterasi beberapa kali setiap fungsi IF
-versi berbasis rata-rata sekitar 4,2% lebih cepat daripada ROUND
versi berbasis.