Wrzucę kapelusz na ring z jeszcze innym podejściem:
Edytuj: Z pewnym opóźnieniem zdaję sobie sprawę, że funkcja Oracle, o której mowa, jako drugi argument przyjmuje ciąg znaków, więc nie spełnia to dokładnie wymagań. Jednak MySQL uprzejmie zdefiniował już 0 - 6 jako poniedziałek - niedziela, a poza tym mam moralne zastrzeżenia do używania łańcucha jako argumentu dla tego typu rzeczy. Ciąg znaków pochodziłby z danych wejściowych użytkownika lub jeszcze jedno mapowanie w kodzie wyższego poziomu między wartościami liczbowymi i łańcuchowymi. Dlaczego nie przekazać liczby całkowitej? :)
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
Aby podzielić część określającą INTERVAL
wartość:
Pierwsza część równania po prostu pobiera przesunięcie między podanym dniem tygodnia a dniem tygodnia podanej daty:
p_weekday - WEEKDAY(p_date)
Zwróci to liczbę dodatnią, jeśli p_weekday
jest większe niż WEEKDAY(p_date)
i wzajemnie. Zero zostanie zwrócone, jeśli będą takie same.
ROUND()
segment służy do określenia, czy żądany dzień tygodnia (p_weekday
) wystąpił już w bieżącym tygodniu w stosunku do daty (p_date
) określone. Na przykład...
ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..zwraca 0
, wskazując, że niedziela (6
) nie miało miejsca w tym tygodniu, ponieważ 2019-01-25
jest piątek. Podobnie...
ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
...zwraca 1
ponieważ środa (2
) już minął. Zauważ, że to zwróci 0
jeśli p_weekday
jest taki sam jak dzień tygodnia p_date
.
Ta wartość (albo 1
lub 0
) jest następnie mnożona przez stałą 7
(liczba dni w tygodniu).
Dlatego jeśli p_weekday
już wystąpił w bieżącym tygodniu, doda 7 do przesunięcia p_weekday - WEEKDAY(p_date)
, ponieważ przesunięcie byłoby liczbą ujemną i chcemy mieć datę w przyszłości.
Jeśli p_weekday
jeszcze nie wystąpił w bieżącym tygodniu, możemy po prostu dodać przesunięcie do bieżącej daty, ponieważ przesunięcie będzie liczbą dodatnią. Stąd sekcja ROUND(...) * 7
jest równy zero i zasadniczo jest ignorowany.
Moim pragnieniem tego podejścia było symulowanie IF()
warunek matematycznie. Byłoby to równie ważne:
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
A w interesie obiektywizmu, uruchamiając 1M iteracji kilka razy dla każdej funkcji, IF
wersja oparta średnio o 4,2% szybciej niż ROUND
wersja oparta.