Tôi sẽ ngả mũ bái phục bằng một cách tiếp cận khác:
Chỉnh sửa: Tôi nhận ra hơi muộn màng rằng hàm Oracle được đề cập lấy một chuỗi làm đối số thứ hai và vì vậy điều này không phù hợp chính xác với yêu cầu. Tuy nhiên MySQL đã vui lòng xác định 0 - 6 là Thứ Hai - Chủ Nhật, và dù sao thì tôi cũng phản đối về mặt đạo đức đối với việc sử dụng một chuỗi làm đối số cho loại điều này. Một chuỗi sẽ đến từ đầu vào của người dùng hoặc một ánh xạ khác trong mã cấp cao hơn giữa các giá trị số và chuỗi. Tại sao không chuyển một số nguyên? :)
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
Để chia nhỏ phần xác định INTERVAL
giá trị:
Phần đầu tiên của phương trình chỉ đơn giản là lấy phần bù giữa ngày trong tuần được chỉ định và ngày trong tuần của ngày được chỉ định:
p_weekday - WEEKDAY(p_date)
Điều này sẽ trả về một số dương nếu p_weekday
lớn hơn WEEKDAY(p_date)
và ngược lại. Số 0 sẽ được trả lại nếu chúng giống nhau.
ROUND()
phân đoạn được sử dụng để xác định xem ngày được yêu cầu trong tuần (p_weekday
) đã xảy ra trong tuần hiện tại so với ngày (p_date
) được chỉ định. Vì vậy, bằng ví dụ ...
ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..returns 0
, cho biết Chủ nhật đó (6
) đã không xảy ra trong tuần này, vì 2019-01-25
là một ngày thứ sáu. Tương tự như vậy ...
ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
... trả về 1
bởi vì Thứ Tư (2
) đã trôi qua. Lưu ý rằng điều này sẽ trả về 0
nếu p_weekday
giống như ngày trong tuần của p_date
.
Giá trị này (1
hoặc 0
) sau đó được nhân với hằng số 7
(số ngày trong tuần).
Do đó nếu p_weekday
đã xảy ra trong tuần hiện tại, nó sẽ thêm 7 vào bù đắp p_weekday - WEEKDAY(p_date)
, bởi vì phần bù đó sẽ là một số âm và chúng tôi muốn có một ngày trong tương lai.
Nếu p_weekday
vẫn chưa xảy ra trong tuần hiện tại, khi đó chúng ta chỉ có thể thêm phần bù vào ngày hiện tại vì phần bù sẽ là một số dương. Do đó, phần ROUND(...) * 7
bằng 0 và về bản chất, bị bỏ qua.
Mong muốn của tôi đối với phương pháp này là mô phỏng một IF()
điều kiện về mặt toán học. Điều này sẽ hợp lệ như nhau:
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
Và vì tính khách quan, khi chạy 1 triệu lần lặp một vài lần của mỗi hàm, IF
phiên bản dựa trên trung bình nhanh hơn khoảng 4,2% so với ROUND
phiên bản dựa trên.