Hodím klobouk do ringu s ještě jiným přístupem:
Upravit: Poněkud opožděně si uvědomuji, že dotyčná funkce Oracle bere řetězec jako druhý argument, a tak to přesně neodpovídá požadavku. MySQL však již laskavě definovalo 0 - 6 jako pondělí - neděle a každopádně mám morální námitky proti použití řetězce jako argumentu pro tento typ věcí. Řetězec by pocházel buď z uživatelského vstupu nebo další mapování v kódu vyšší úrovně mezi číselnými a řetězcovými hodnotami. Proč nepředat celé číslo? :)
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
Rozdělit část, která určuje INTERVAL
hodnota:
První část rovnice jednoduše získá posun mezi zadaným dnem v týdnu a dnem v týdnu zadaného data:
p_weekday - WEEKDAY(p_date)
Toto vrátí kladné číslo, pokud p_weekday
je větší než WEEKDAY(p_date)
a naopak. Pokud jsou stejné, bude vrácena nula.
ROUND()
segment se používá k určení, zda požadovaný den v týdnu (p_weekday
) již došlo v aktuálním týdnu vzhledem k datu (p_date
) specifikováno. Takže například...
ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..vrací 0
, což označuje tu neděli (6
) tento týden nenastal, jako 2019-01-25
je pátek. Stejně tak...
ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
...vrací 1
protože středa (2
) již prošel. Všimněte si, že to vrátí 0
pokud p_weekday
je stejný jako den v týdnu p_date
.
Tato hodnota (buď 1
nebo 0
) se pak vynásobí konstantou 7
(počet dní v týdnu).
Pokud tedy p_weekday
již došlo v aktuálním týdnu, přidá 7 k offsetu p_weekday - WEEKDAY(p_date)
, protože tento posun by byl záporné číslo a my chceme datum v budoucnosti.
Pokud p_weekday
ještě nenastane v aktuálním týdnu, pak můžeme jen přidat posun k aktuálnímu datu, protože posun bude kladné číslo. Proto sekce ROUND(...) * 7
se rovná nule a v podstatě se ignoruje.
Mým přáním tohoto přístupu bylo simulovat IF()
podmínka matematicky. To by bylo stejně platné:
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
A v zájmu objektivity při spuštění 1M iterací několikrát každé funkce IF
-založená verze byla v průměru o 4,2 % rychlejší než ROUND
-založená verze.