Ho intenzione di lanciare il mio cappello sul ring con l'ennesimo approccio:
Modifica: Mi rendo conto un po' tardivamente che la funzione Oracle in questione accetta una stringa come secondo argomento, e quindi questo non si adatta esattamente al requisito. Tuttavia MySQL ha già gentilmente definito 0 - 6 come lunedì - domenica, e comunque ho obiezioni morali all'utilizzo di una stringa come argomento per questo tipo di cose. Una stringa proverrebbe dall'input dell'utente o ancora un'altra mappatura nel codice di livello superiore tra valori numerici e stringa. Perché non passare un numero intero? :)
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
Per scomporre la parte che determina l'INTERVAL
valore:
La prima parte dell'equazione ottiene semplicemente l'offset tra il giorno della settimana specificato e il giorno della settimana della data specificata:
p_weekday - WEEKDAY(p_date)
Questo restituirà un numero positivo se p_weekday
è maggiore di WEEKDAY(p_date)
e viceversa. Zero verrà restituito se sono uguali.
Il ROUND()
segment viene utilizzato per determinare se il giorno della settimana richiesto (p_weekday
) è già avvenuto nella settimana corrente rispetto alla data (p_date
) specificato. Quindi, per esempio...
ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..restituisce 0
, indicando quella domenica (6
) non si è verificato questa settimana, come 2019-01-25
è un venerdì. Allo stesso modo...
ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
...restituisce 1
perché mercoledì (2
) è già passato. Nota che questo restituirà 0
se p_weekday
è lo stesso del giorno della settimana di p_date
.
Questo valore (o 1
o 0
) viene quindi moltiplicato per la costante 7
(il numero di giorni in una settimana).
Quindi se p_weekday
è già avvenuto nella settimana corrente, aggiungerà 7 all'offset p_weekday - WEEKDAY(p_date)
, perché quell'offset sarebbe un numero negativo e vogliamo una data futura.
Se p_weekday
deve ancora verificarsi nella settimana corrente, quindi possiamo semplicemente aggiungere l'offset alla data corrente perché l'offset sarà un numero positivo. Da qui la sezione ROUND(...) * 7
è uguale a zero e, in sostanza, ignorato.
Il mio desiderio per questo approccio era simulare un IF()
condizione matematicamente. Questo sarebbe ugualmente valido:
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
E nell'interesse dell'obiettività, nell'eseguire 1M iterazioni alcune volte di ciascuna funzione, il IF
-based versione mediamente più veloce di circa il 4,2% rispetto a ROUND
versione basata su.