Vou jogar meu chapéu no ringue com mais uma abordagem:
Editar: Percebo um pouco tardiamente que a função Oracle em questão recebe uma string como segundo argumento e, portanto, isso não se encaixa precisamente no requisito. No entanto, o MySQL já gentilmente definiu 0 - 6 como segunda-feira - domingo, e de qualquer maneira tenho objeções morais em usar uma string como argumento para esse tipo de coisa. Uma string viria da entrada do usuário ou ainda outro mapeamento em código de nível superior entre valores numéricos e de string. Por que não passar um inteiro? :)
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
Para dividir a parte que determina o
INTERVAL
valor:A primeira parte da equação simplesmente obtém o deslocamento entre o dia da semana especificado e o dia da semana da data especificada:
p_weekday - WEEKDAY(p_date)
Isso retornará um número positivo se
p_weekday
é maior que WEEKDAY(p_date)
e vice versa. Zero será devolvido se forem iguais. O
ROUND()
segmento é usado para determinar se o dia da semana solicitado (p_weekday
) já ocorreu na semana atual em relação à data (p_date
) Especificadas. Então, por exemplo... ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..retorna
0
, indicando que domingo (6
) não ocorreu esta semana, pois 2019-01-25
é uma sexta-feira. Da mesma maneira... ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
...retorna
1
porque quarta-feira (2
) já passou. Observe que isso retornará 0
se p_weekday
é igual ao dia da semana de p_date
. Este valor (ou
1
ou 0
) é então multiplicado pela constante 7
(o número de dias em uma semana). Portanto, se
p_weekday
já ocorreu na semana atual, adicionará 7 ao deslocamento p_weekday - WEEKDAY(p_date)
, porque esse deslocamento seria um número negativo e queremos uma data no futuro. Se
p_weekday
ainda não ocorreu na semana atual, podemos apenas adicionar o deslocamento à data atual porque o deslocamento será um número positivo. Daí a seção ROUND(...) * 7
é igual a zero e, em essência, ignorado. Meu desejo com essa abordagem era simular um
IF()
condição matematicamente. Isso seria igualmente válido: RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
E no interesse da objetividade, ao executar 1 milhão de iterações algumas vezes de cada função, o
IF
versão baseada em média cerca de 4,2% mais rápida que a ROUND
versão baseada.