Jag ska kasta min hatt i ringen med ännu ett tillvägagångssätt:
Redigera: Jag inser något försenat att Oracle-funktionen i fråga tar en sträng som det andra argumentet, så detta passar inte exakt kravet. Men MySQL har redan vänligt definierat 0 - 6 som måndag - söndag, och hur som helst har jag moraliska invändningar mot att använda en sträng som argument för den här typen av saker. En sträng kommer antingen från användarinmatning eller ännu en mappning i kod på högre nivå mellan numeriska och strängvärden. Varför inte passera ett heltal? :)
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
För att dela upp delen som bestämmer INTERVAL
värde:
Den första delen av ekvationen får helt enkelt offset mellan den angivna veckodagen och veckodagen för det angivna datumet:
p_weekday - WEEKDAY(p_date)
Detta returnerar ett positivt tal om p_weekday
är större än WEEKDAY(p_date)
och vice versa. Noll kommer att returneras om de är likadana.
ROUND()
segment används för att avgöra om den begärda veckodagen (p_weekday
) har redan inträffat under den aktuella veckan i förhållande till datumet (p_date
) specificerad. Så, till exempel...
ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..returerar 0
, vilket indikerar den söndagen (6
) har inte inträffat den här veckan, eftersom 2019-01-25
är en fredag. Likaså...
ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
...returerar 1
eftersom onsdag (2
) har redan gått. Observera att detta returnerar 0
om p_weekday
är samma som veckodagen p_date
.
Detta värde (antingen 1
eller 0
) multipliceras sedan med konstanten 7
(antal dagar i en vecka).
Därför om p_weekday
redan har inträffat under den aktuella veckan kommer det att lägga till 7 till offset p_weekday - WEEKDAY(p_date)
, eftersom den offset skulle vara ett negativt tal och vi vill ha ett datum i framtiden.
Om p_weekday
har ännu inte inträffat under den aktuella veckan, så kan vi bara lägga till offset till det aktuella datumet eftersom offset kommer att vara ett positivt tal. Därav avsnittet ROUND(...) * 7
är lika med noll och ignoreras i huvudsak.
Min önskan med detta tillvägagångssätt var att simulera en IF()
tillstånd matematiskt. Detta skulle vara lika giltigt:
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
Och i objektivitetens intresse, när du kör 1 miljon iterationer några gånger av varje funktion, visas IF
-baserad version var i genomsnitt cirka 4,2 % snabbare än ROUND
-baserad version.