sumar - sql funcion dias habiles
Función MySQL para encontrar el número de días hábiles entre dos fechas (29)
¿Podrían las cuerdas propuestas ser incorrectas?
DATEDIFF (de, a) excluye ''a''. De la misma manera también debería esta cadena:
Lunes -> viernes = {lun, tu, mié, th} = 4
Lunes -> Sábado = {Lunes, Miércoles, Miércoles, Jueves, Viernes} = 5
Martes -> Lunes = {Tu, Mie, Jue, Vie, sáltate Sáb, omite Sol, Mon está excluido} = 4
y así
Matriz propuesta:
| M T W T F S S
-|--------------
M| 0 1 2 3 4 5 5
T| 4 0 1 2 3 4 4
W| 3 4 0 1 2 3 3
T| 2 3 4 0 1 2 2
F| 1 2 3 4 0 1 1
S| 0 1 2 3 4 0 0
S| 0 1 2 3 4 5 0
Cadena: ''0123455401234434012332340122123401101234000123450''
¿Me estoy perdiendo de algo? :)
Excel tiene la función NETWORKDAYS () que determina el número de días hábiles entre dos fechas.
¿Alguien tiene una función similar para MySQL? Dado que las vacaciones añaden complejidad, la solución no tiene que lidiar con las vacaciones.
Aunque es un mensaje VIEJO pero ayuda mucho. Según la solución proporcionada por @shahcool, no está devolviendo días exactos, por ejemplo
Workdays(''2013-03-26'',''2013-04-01'')
Devuelve 3
días pero en realidad debe ser 5
días
A continuación se muestra la solución que he probado y Retruntar días laborables exactos
DELIMITER $$
DROP FUNCTION IF EXISTS WORKDAYS $$
CREATE FUNCTION `WORKDAYS` (first_date DATETIME, second_date DATETIME) RETURNS INT
LANGUAGE SQL
DETERMINISTIC
BEGIN
DECLARE start_date DATE;
DECLARE end_date DATE;
DECLARE diff INT;
DECLARE NumberOfWeeks INT;
DECLARE RemainingDays INT;
DECLARE firstDayOfTheWeek INT;
DECLARE lastDayOfTheWeek INT;
DECLARE WorkingDays INT;
IF (first_date < second_date) THEN
SET start_date = first_date;
SET end_date = second_date;
ELSE
SET start_date = second_date;
SET end_date = first_date;
END IF;
## Add one to include both days in interval
SET diff = DATEDIFF(end_date, start_date)+1;
SET NumberOfWeeks=floor(diff/7);
SET RemainingDays=MOD(diff,7);
SET firstDayOfTheWeek=DAYOFWEEK(start_date);
SET lastDayOfTheWeek=DAYOFWEEK(end_date);
IF(firstDayOfTheWeek <= lastDayOfTheWeek) THEN
IF( firstDayOfTheWeek<=6 AND 6 <=lastDayOfTheWeek) THEN SET RemainingDays=RemainingDays-1; END IF;
IF( firstDayOfTheWeek<=7 AND 7 <=lastDayOfTheWeek) THEN SET RemainingDays=RemainingDays-1; END IF;
ELSE
IF( firstDayOfTheWeek=7) THEN SET RemainingDays=RemainingDays-1;
IF (lastDayOfTheWeek=6) THEN SET RemainingDays=RemainingDays-1; END IF;
ELSE SET RemainingDays=RemainingDays-2;
END IF;
END IF;
SET WorkingDays=NumberOfWeeks*5;
IF(RemainingDays>0) THEN RETURN WorkingDays+RemainingDays;
ELSE RETURN WorkingDays; END IF;
END $$
DELIMITER ;
Como necesitará realizar un seguimiento de las vacaciones en algún lugar, una tabla de Calendario parece apropiada:
CREATE TABLE Calendar
(
calendar_date DATETIME NOT NULL,
is_holiday BIT NOT NULL,
is_weekend BIT NOT NULL,
CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (calendar_date)
)
Por supuesto, debe llenarlo con todas las fechas para cualquier período de tiempo que pueda trabajar en su aplicación. Como solo hay 365 (o 366) días en un año, pasar de 1900 a 2100 no es un gran problema. Solo asegúrate de cargarlo con todas las fechas, no solo las vacaciones.
En ese momento, las consultas como la que necesita se vuelven triviales:
SELECT
COUNT(*)
FROM
Calendar
WHERE
calendar_date BETWEEN ''2009-01-01'' AND ''2009-10-01'' AND
is_holiday = 0 AND
is_weekend = 0
Advertencia: trabajo principalmente con MS SQL y no he trabajado con MySQL en mucho tiempo, por lo que es posible que tengas que modificar lo anterior. Por ejemplo, ni siquiera recuerdo si MySQL tiene el tipo de datos BIT.
Dado el primer día de un mes, esto devolverá el número de días laborables dentro de ese mes. En MySQL Sin un procedimiento almacenado.
SELECT (DATEDIFF(LAST_DAY(?),?) + 1) -
((WEEK(LAST_DAY(?)) - WEEK(?)) * 2) -
(case when weekday(?) = 6 then 1 else 0 end) -
(case when weekday(LAST_DAY(?)) = 5 then 1 else 0 end)
El problema que tendrá con el par "ignorar las vacaciones" es que cada país tendrá vacaciones diferentes.
Deberá comenzar por definir las vacaciones de su país y luego pasarlas para ver si una fecha determinada es feriado.
No sé de funciones genéricas que hagan lo que quieras en mysql
¡Lo siento!
En función de la función anterior de Yada, aquí hay una ligera variación del tema, que calcula los días de trabajo restantes desde la fecha actual (sin incluir) hasta la fecha objetivo. También maneja los diferentes días de fin de semana en Israel :-) Tenga en cuenta que esto producirá un resultado negativo si la fecha objetivo está en el pasado (que es justo lo que quería).
DELIMITER //
DROP FUNCTION IF EXISTS WORKDAYS_LEFT//
CREATE FUNCTION WORKDAYS_LEFT(target_date DATE, location char(2))
RETURNS INT
LANGUAGE SQL
DETERMINISTIC
BEGIN
DECLARE start_date DATE;
DECLARE end_date DATE;
DECLARE check_date DATE;
DECLARE diff INT;
DECLARE extra_weekend_days INT;
DECLARE weeks_diff INT;
SET start_date = CURDATE();
SET end_date = target_date;
SET diff = DATEDIFF(end_date, start_date);
SET weeks_diff = FLOOR(diff / 7);
SET end_date = DATE_SUB(end_date, INTERVAL (weeks_diff * 7) DAY);
SET check_date = DATE_ADD(start_date, INTERVAL 1 DAY);
SET extra_weekend_days = 0;
WHILE check_date <= end_date DO
SET extra_weekend_days = extra_weekend_days +
IF(DAYNAME(check_date) = ''Saturday'', 1, 0) +
IF(DAYNAME(check_date) = IF(location = ''IL'',''Friday'', ''Sunday''), 1, 0);
SET check_date = DATE_ADD(check_date, INTERVAL 1 DAY);
END WHILE;
RETURN diff - weeks_diff*2 - extra_weekend_days;
END//
DELIMITER ;
Esta consulta devuelve fácilmente el número de días hábiles entre dos fechas que excluyen los fines de semana:
select datediff(''2016-06-19'',''2016-06-01'') - (floor(datediff(''2016-06-19'',''2016-06-01'')/6) + floor(datediff(''2016-06-19'',''2016-06-01'')/7));
Esta es una caída en reemplazo de DATEDIFF que funciona para las diferencias + ve y -ve.
DELIMITER $$
DROP FUNCTION IF EXISTS WORKDAYSDIFF$$
CREATE FUNCTION WORKDAYSDIFF(sd DATE, ed DATE)
RETURNS INT
LANGUAGE SQL
DETERMINISTIC
BEGIN
RETURN IF (sd >= ed,
5 * (DATEDIFF(sd, ed) DIV 7) + MID(''0123455501234445012333450122234501101234000123450'', 7 * WEEKDAY(ed) + WEEKDAY(sd) + 1, 1),
-(5 * (DATEDIFF(ed, sd) DIV 7) + MID(''0123455501234445012333450122234501101234000123450'', 7 * WEEKDAY(sd) + WEEKDAY(ed) + 1, 1)) );
END$$
DELIMITER ;
Esta expresión -
5 * (DATEDIFF(@E, @S) DIV 7) + MID(''0123444401233334012222340111123400012345001234550'', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1)
calcula el número de días hábiles entre la fecha de inicio @S y la fecha de finalización @E.
Asume que la fecha de finalización (@E) no es anterior a la fecha de inicio (@S). Compatible con DATEDIFF en que la misma fecha de inicio y fecha de finalización le da cero días hábiles. Ignora las vacaciones.
La cadena de dígitos se construye de la siguiente manera. Cree una tabla de días de inicio y días de finalización, las filas deben comenzar el lunes (DÍA DE LA SEMANA 0) y las columnas deben comenzar también el lunes. Complete la diagonal desde la esquina superior izquierda a la esquina inferior derecha con todos los 0 (es decir, hay 0 días hábiles entre lunes y lunes, martes y martes, etc.). Para cada día, comience por la diagonal (siempre debe ser 0) y complete las columnas a la derecha, un día a la vez. Si aterriza en una columna de día de fin de semana (día no laborable), el número de días hábiles no cambia, se transfiere desde la izquierda. De lo contrario, el número de días hábiles aumenta en uno. Cuando llegue al final del ciclo de la fila de nuevo al comienzo de la misma fila y continúe hasta llegar a la diagonal de nuevo. Luego pasa a la siguiente fila.
Ej. Suponer que el sábado y el domingo no son días hábiles,
| M T W T F S S
-|--------------
M| 0 1 2 3 4 4 4
T| 4 0 1 2 3 3 3
W| 3 4 0 1 2 2 2
T| 2 3 4 0 1 1 1
F| 1 2 3 4 0 0 0
S| 1 2 3 4 5 0 0
S| 1 2 3 4 5 5 0
Luego concatenar los 49 valores de la tabla en la cadena.
Por favor, avíseme si encuentra algún error.
-Editar tabla mejorada:
| M T W T F S S
-|--------------
M| 0 1 2 3 4 4 4
T| 4 0 1 2 3 3 3
W| 3 4 0 1 2 2 2
T| 2 3 4 0 1 1 1
F| 1 2 3 4 0 0 0
S| 0 1 2 3 4 0 0
S| 0 1 2 3 4 4 0
cadena mejorada: ''0123444401233334012222340111123400001234000123440''
expresión mejorada:
5 * (DATEDIFF(@E, @S) DIV 7) + MID(''0123444401233334012222340111123400001234000123440'', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1)
Esta solución utiliza básicamente el mismo enfoque que la de Rodger, excepto que el método para generar la matriz es mucho más complejo. Nota: Esta salida de esta solución no es compatible con NETWORKDAYS.
Al igual que en la solución de Rodger, esto calcula el número de días hábiles entre la fecha de inicio (@S) y la fecha de finalización (@E) sin tener que definir un procedimiento almacenado. Supone que la fecha de finalización no es anterior a la fecha de inicio. Usar la misma fecha de inicio y final producirá 0. Las vacaciones no se tienen en cuenta.
La principal diferencia entre esto y la solución de Rodger es que la matriz y la cadena resultante de dígitos está construida por un complejo algoritmo que no he incluido. La salida de este algoritmo se valida mediante una prueba unitaria (ver las entradas y salidas de prueba a continuación). En la matriz, la intersección de cualquier par de valores xyy (WEEKDAY (@S) y WEEKDAY (@E) arroja la diferencia en días de trabajo entre los dos valores. El orden de asignación no es importante ya que los dos se suman para trazar la posición.
Los días hábiles son de lunes a viernes
| M T W T F S S
-|--------------
M| 0 1 2 3 4 5 5
T| 5 0 1 2 3 4 4
W| 4 5 0 1 2 3 3
T| 3 4 5 0 1 2 2
F| 2 3 4 5 0 1 1
S| 0 1 2 3 4 0 0
S| 0 1 2 3 4 5 0
Los 49 valores de la tabla se concatenan en la siguiente cadena:
0123455501234445012333450122234501101234000123450
Al final, la expresión correcta es:
5 * (DATEDIFF(@E, @S) DIV 7) + MID(''0123455501234445012333450122234501101234000123450'', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1)
He verificado las siguientes entradas y salidas usando esta solución:
Sunday, 2012-08-26 -> Monday, 2012-08-27 = 0
Sunday, 2012-08-26 -> Sunday, 2012-09-02 = 5
Monday, 2012-08-27 -> Tuesday, 2012-08-28 = 1
Monday, 2012-08-27 -> Monday, 2012-09-10 = 10
Monday, 2012-08-27 -> Monday, 2012-09-17 = 15
Monday, 2012-08-27 -> Tuesday, 2012-09-18 = 16
Monday, 2012-08-27 -> Monday, 2012-09-24 = 20
Monday, 2012-08-27 -> Monday, 2012-10-01 = 25
Tuesday, 2012-08-28 -> Wednesday, 2012-08-29 = 1
Wednesday, 2012-08-29 -> Thursday, 2012-08-30 = 1
Thursday, 2012-08-30 -> Friday, 2012-08-31 = 1
Friday, 2012-08-31 -> Saturday, 2012-09-01 = 1
Saturday, 2012-09-01 -> Sunday, 2012-09-02 = 0
Sunday, 2012-09-02 -> Monday, 2012-09-03 = 0
Monday, 2012-09-03 -> Tuesday, 2012-09-04 = 1
Tuesday, 2012-09-04 -> Wednesday, 2012-09-05 = 1
Wednesday, 2012-09-05 -> Thursday, 2012-09-06 = 1
Thursday, 2012-09-06 -> Friday, 2012-09-07 = 1
Friday, 2012-09-07 -> Saturday, 2012-09-08 = 1
Saturday, 2012-09-08 -> Sunday, 2012-09-09 = 0
Monday, 2012-09-24 -> Sunday, 2012-10-07 = 10
Saturday, 2012-08-25 -> Saturday, 2012-08-25 = 0
Saturday, 2012-08-25 -> Sunday, 2012-08-26 = 0
Saturday, 2012-08-25 -> Monday, 2012-08-27 = 0
Saturday, 2012-08-25 -> Tuesday, 2012-08-28 = 1
Saturday, 2012-08-25 -> Wednesday, 2012-08-29 = 2
Saturday, 2012-08-25 -> Thursday, 2012-08-30 = 3
Saturday, 2012-08-25 -> Friday, 2012-08-31 = 4
Saturday, 2012-08-25 -> Sunday, 2012-09-02 = 0
Monday, 2012-08-27 -> Monday, 2012-08-27 = 0
Monday, 2012-08-27 -> Tuesday, 2012-08-28 = 1
Monday, 2012-08-27 -> Wednesday, 2012-08-29 = 2
Monday, 2012-08-27 -> Thursday, 2012-08-30 = 3
Monday, 2012-08-27 -> Friday, 2012-08-31 = 4
Monday, 2012-08-27 -> Saturday, 2012-09-01 = 5
Monday, 2012-08-27 -> Sunday, 2012-09-02 = 5
Esto funciona en Sql Server 2005
No sé si te va a funcionar.
DECLARE @StartDate DATETIME,
@EndDate DATETIME
SELECT @StartDate = ''22 Nov 2009'',
@EndDate = ''28 Nov 2009''
;WITH CTE AS(
SELECT @StartDate DateVal,
DATENAME(dw, @StartDate) DayNameVal
UNION ALL
SELECT DateVal + 1,
DATENAME(dw, DateVal + 1)
FROM CTE
WHERE DateVal < @EndDate
)
SELECT COUNT(1)
FROM (
SELECT *
FROM CTE
WHERE DayNameVal NOT IN (''Sunday'',''Saturday'')
) DayVals
Función MYSQL que devuelve días hábiles entre 2 fechas (inclusive). BETWEEN 2 AND 6 es de lunes a viernes, esto se puede ajustar según su calendario / región.
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`@`localhost` FUNCTION `fn_GetBusinessDaysBetweenDates`(d1 DATE, d2 DATE) RETURNS int(11)
BEGIN
DECLARE bDaysInPeriod INT;
SET bDaysInPeriod=0;
WHILE d1<=d2 DO
IF DAYOFWEEK(d1) BETWEEN 2 AND 6 THEN
SET bDaysInPeriod=bDaysInPeriod+1;
END IF;
SET d1=d1+INTERVAL 1 day;
END WHILE;
RETURN bDaysInPeriod;
END
Helooo prueba por favor.
DELIMITER $$
DROP FUNCTION IF EXISTS `WORKDAYS` $$
CREATE FUNCTION `WORKDAYS` (first_date DATETIME, second_date DATETIME) RETURNS INT
LANGUAGE SQL
DETERMINISTIC
BEGIN
DECLARE start_date DATE;
DECLARE end_date DATE;
DECLARE diff INT;
DECLARE cnt INT;
IF (first_date < second_date) THEN
SET start_date = first_date;
SET end_date = second_date;
ELSE
SET start_date = second_date;
SET end_date = first_date;
END IF;
SELECT COUNT(*) INTO cnt FROM `holiday` WHERE (hday BETWEEN start_date AND end_date) and (DAYOFWEEK(hday) != 7 and DAYOFWEEK(hday) != 1);
SET diff = DATEDIFF(end_date, start_date) ;
RETURN (CASE WHEN DAYNAME(start_date) not in (''Saturday'', ''Sunday'') && DAYNAME(end_date) = ''Saturday'' THEN (diff - cnt)
WHEN DAYNAME(start_date) not in (''Saturday'', ''Sunday'') && DAYNAME(end_date) = ''Sunday'' THEN (diff - 2 - cnt)
WHEN DAYNAME(start_date) = ''Saturday'' && DAYNAME(end_date) = ''Sunday'' THEN (diff - 1 - cnt)
WHEN DAYNAME(start_date) = ''Saturday'' && DAYNAME(end_date) = ''Saturday'' THEN (diff + 1 - cnt)
WHEN DAYNAME(start_date) = ''Sunday'' && DAYNAME(end_date) in (''Saturday'', ''Sunday'') THEN (diff + 1 - cnt)
WHEN DAYNAME(start_date) = ''Saturday'' && DAYNAME(end_date) not in (''Saturday'', ''Sunday'') THEN (diff -1 - cnt)
WHEN DAYNAME(start_date) = ''Sunday'' && DAYNAME(end_date) not in (''Saturday'', ''Sunday'') THEN (diff + 1 - cnt)
WHEN DAYNAME(start_date) not in (''Saturday'', ''Sunday'') && DAYNAME(end_date) not in (''Saturday'', ''Sunday'')
&& WEEKDAY(start_date) > WEEKDAY(end_date) THEN (diff - 2 - cnt)
ELSE (diff - cnt) END)
- (FLOOR(diff / 7) * 2)
- (CASE WHEN DAYNAME(start_date) = ''Sunday'' THEN 1 ELSE 0 END)
- (CASE WHEN DAYNAME(end_date) = ''Saturday'' THEN 1 ELSE 0 END);
END $$
y vacaciones de mesa
DROP TABLE IF EXISTS `holiday`;
CREATE TABLE `holiday` (
`id` bigint(32) unsigned NOT NULL AUTO_INCREMENT,
`hday` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `holiday` (`id`, `hday`) VALUES
(1, ''2012-01-01''),
(2, ''2012-05-01''),
(3, ''2012-05-08''),
(4, ''2012-07-05''),
(5, ''2012-07-06''),
(6, ''2012-09-28''),
(7, ''2012-10-28''),
(8, ''2012-11-17''),
(9, ''2012-12-24''),
(10, ''2012-12-25''),
(11, ''2012-12-26'');
etc...
La diferencia de días sin fines de semana se puede lograr de esta manera:
CREATE FUNCTION `WDDIFF` (d0 DATE, d1 DATE)
RETURNS INT DETERMINISTIC
COMMENT ''Date0, Date1''
BEGIN
RETURN DATEDIFF(d1, d0) - (DATEDIFF(DATE_SUB(d1, INTERVAL WEEKDAY(d1) DAY), DATE_ADD(d0, INTERVAL (7 - WEEKDAY(d0)) DAY))/7+1)*2 + IF(WEEKDAY(d0)>4, 1, 0) + 1;
END
Uso: días de la semana desde el comienzo del mes
SELECT ap.WDDIFF(DATE_SUB(CURDATE(), INTERVAL DAYOFMONTH(CURDATE()) - 1 DAY), CURDATE())
Nota: La función cuenta las fechas de inicio y finalización
La respuesta principal se contabilizó para los días comprendidos entre la fecha de inicio y la fecha de finalización pero excluyó la fecha de finalización.
También para las fechas que comenzaron y terminan en los mismos días de fin de semana, por ejemplo, el sábado 2018-05-05 al sábado 2018-05-12, se calculó un día más.
¡Aquí hay una función que funciona perfectamente para mí!
drop procedure if exists get_duration$$
create procedure get_duration(in data_from date, in data_to date)
begin
if (WEEKDAY(data_from) = 5 AND WEEKDAY(data_to) = 5)
OR (WEEKDAY(data_from) = 6 AND WEEKDAY(data_to) = 6) then
select (5 * (DATEDIFF(data_to, data_from) DIV 7)
+ MID(''0123444401233334012222340111123400001234000123440'',
7 * WEEKDAY(data_from) + WEEKDAY(data_to) + 1, 1)) dur;
else
select (5 * (DATEDIFF(data_to, data_from) DIV 7)
+ MID(''0123444401233334012222340111123400001234000123440'',
7 * WEEKDAY(data_from) + WEEKDAY(data_to) + 1, 1))+1 dur;
end if;
end$$
La solución de Yada no funciona correctamente. Mis cambios:
DELIMITER $$
DROP FUNCTION IF EXISTS `catalog`.`WORKDAYS` $$
CREATE FUNCTION `catalog`.`WORKDAYS` (first_date DATETIME, second_date DATETIME) RETURNS INT
LANGUAGE SQL
DETERMINISTIC
BEGIN
DECLARE start_date DATE;
DECLARE end_date DATE;
DECLARE diff INT;
IF (first_date < second_date) THEN
SET start_date = first_date;
SET end_date = second_date;
ELSE
SET start_date = second_date;
SET end_date = first_date;
END IF;
SET diff = DATEDIFF(end_date, start_date);
RETURN (CASE WHEN DAYNAME(start_date) not in (''Saturday'', ''Sunday'') && DAYNAME(end_date) = ''Saturday'' THEN diff
WHEN DAYNAME(start_date) not in (''Saturday'', ''Sunday'') && DAYNAME(end_date) = ''Sunday'' THEN (diff - 2)
WHEN DAYNAME(start_date) = ''Saturday'' && DAYNAME(end_date) = ''Sunday'' THEN (diff - 1)
WHEN DAYNAME(start_date) = ''Saturday'' && DAYNAME(end_date) = ''Saturday'' THEN (diff + 1)
WHEN DAYNAME(start_date) = ''Sunday'' && DAYNAME(end_date) in (''Saturday'', ''Sunday'') THEN (diff + 1)
WHEN DAYNAME(start_date) = ''Saturday'' && DAYNAME(end_date) not in (''Saturday'', ''Sunday'') THEN (diff -1)
WHEN DAYNAME(start_date) = ''Sunday'' && DAYNAME(end_date) not in (''Saturday'', ''Sunday'') THEN (diff + 1)
WHEN DAYNAME(start_date) not in (''Saturday'', ''Sunday'') && DAYNAME(end_date) not in (''Saturday'', ''Sunday'')
&& WEEKDAY(start_date) > WEEKDAY(end_date) THEN (diff - 2)
ELSE diff END)
- (FLOOR(diff / 7) * 2)
- (CASE WHEN DAYNAME(start_date) = ''Sunday'' THEN 1 ELSE 0 END)
- (CASE WHEN DAYNAME(end_date) = ''Saturday'' THEN 1 ELSE 0 END);
END $$
DELIMITER ;
Necesitaba dos funciones. Uno para calcular el número de días hábiles entre dos fechas y una para sumar / restar x días hábiles a una fecha. Esto es lo que reuní de los ejemplos que encontré en Internet. Están diseñados para estar cerca de las funciones estándar DATEDIFF () y DATE_ADD (), así como para complementar los cálculos de los demás. Por ejemplo, DateDiffBusiness (''2014-05-14'', DateAddBusiness (''2014-05-14'', 5)) será igual a 5.
DROP FUNCTION IF EXISTS DateDiffBusiness;
DELIMITER &
CREATE FUNCTION DateDiffBusiness( d2 DATE, d1 DATE )
RETURNS INT
DETERMINISTIC
COMMENT ''Calculates the number of bussiness days between two dates''
BEGIN
DECLARE dow1, dow2, days INT;
SET dow1 = DAYOFWEEK(d1);
SET dow2 = DAYOFWEEK(d2);
SET days = FLOOR( DATEDIFF(d2,d1)/7 ) * 5 +
CASE
WHEN dow1=1 AND dow2=7 THEN 5
WHEN dow1 IN(7,1) AND dow2 IN (7,1) THEN 0
WHEN dow1=dow2 THEN 1
WHEN dow1 IN(7,1) AND dow2 NOT IN (7,1) THEN dow2-1
WHEN dow1 NOT IN(7,1) AND dow2 IN(7,1) THEN 7-dow1
WHEN dow1<=dow2 THEN dow2-dow1+1
WHEN dow1>dow2 THEN 5-(dow1-dow2-1)
ELSE 0
END;
RETURN days-1;
END&
DELIMITER ;
DROP FUNCTION IF EXISTS DateAddBusiness;
DELIMITER &
CREATE FUNCTION DateAddBusiness(mydate DATE, numday INT)
RETURNS DATE
DETERMINISTIC
COMMENT ''Adds bussiness days between two dates''
BEGIN
DECLARE num_week INT DEFAULT 0;
DECLARE num_day INT DEFAULT 0;
DECLARE adj INT DEFAULT 0;
DECLARE total INT DEFAULT 0;
SET num_week = numday DIV 5;
SET num_day = MOD(numday, 5);
IF (WEEKDAY(mydate) + num_day >= 5) then
SET adj = 2;
END IF;
SET total = num_week * 7 + adj + num_day;
RETURN DATE_ADD(mydate, INTERVAL total DAY);
END&
DELIMITER ;
Para la función NETWORKDAYS () anterior, se debe agregar una condición más para cubrir casos cuando la fecha de inicio a la fecha de finalización es dentro de los 7 días y durante un fin de semana.
RETURN (diff + 1)
- (FLOOR(diff / 7) * 2)
- (CASE WHEN DAYNAME(start_date) = ''Sunday'' THEN 1 ELSE 0 END)
- (CASE WHEN DAYNAME(end_date) = ''Saturday'' THEN 1 ELSE 0 END)
- (CASE WHEN diff<7 and WEEK(start_date)<>WEEK(end_date) THEN 2 ELSE 0 end);
Respuesta publicada por @Rodger Bagnall no funciona correctamente para mí, por ejemplo, en 2016-04. Muestra 1 día menos que en real.
si hablamos de calcular por consulta, uso esto:
set
@S = ''2016-04-01'',
@E = ''2016-04-30'';
select
case
when WEEKDAY(@S) < 5 then 5 - WEEKDAY(@S)
else 0
end #startweek
+
case
when WEEKDAY(@E) < 5 then WEEKDAY(@E) + 1
else 5
end #endweek
+
(
DATEDIFF(@E, @S) + 1 # plus 1 day cause params is inside 1 month
- (7 - WEEKDAY(@S)) # minus start week
- (WEEKDAY(@E) + 1) # minus end week
) DIV 7 * 5 #rest part
as work_date_count;
Consulta no optimizada solo para mostrar de dónde vienen los números
Sé que este es un hilo viejo, pero pensaba que mi solución podría ser útil para algunas personas. esta es una consulta que hice para encontrar los días de trabajo sin la necesidad de funciones. puede asignarle el nombre que desee a los campos, simplemente los dejé en blanco a propósito.
SELECT
@tmp_s := ept.`date_start`,
@tmp_e := IF(ept.`date_end` IS NULL, NOW(),ept.`date_end`),
@start := IF(DAYOFWEEK(@tmp_s)=1,@tmp_s + INTERVAL 1 DAY,(IF(DAYOFWEEK(@tmp_s)=7,@tmp_s + INTERVAL 2 DAY,@tmp_s)),
@end := IF(DAYOFWEEK(@tmp_e)=1,@tmp_e - INTERVAL 2 DAY,(IF(DAYOFWEEK(@tmp_e)=7,@tmp_e - INTERVAL 1 DAY,@tmp_e)),
@bizdays := CASE
WHEN DATEDIFF(@end,@start)>7 THEN CEIL((DATEDIFF(@end,@start)/7)*5)
WHEN DAYOFWEEK(@end)< DAYOFWEEK(@start) THEN DATEDIFF(@end,@start)-2
ELSE DATEDIFF(@end,@start)
END,
DATE(@start),
DATE(@end),
IF(@bizdays>=10,10,@bizdays)
FROM `employee_points` ept
WHERE ept.`date_start` > ''2011-01-01''
Si realmente quiere ignorar la existencia de los fines de semana, debe tratar algo que se origina en Sat / Sun como si se originó en lunes; y algo que termina el sábado / domingo como si realmente terminara el viernes. Por lo tanto, algo que comienza y termina en un fin de semana, debe ignorar tanto el inicio como el final. No creo que ninguna de las otras respuestas haga esto.
La siguiente función hace esto:
CREATE DEFINER=`root`@`localhost` FUNCTION `weekdayDiff`
(
edate datetime,
sdate datetime
)
RETURNS int
DETERMINISTIC
BEGIN
if edate>sdate
then
return 5 * (DATEDIFF(edate, sdate) DIV 7) + MID(''+0+1+2+3+4+4+4+4+0+1+2+3+3+3+3+4+0+1+2+2+2+2+3+4+0+1+1+1+1+2+3+4+0+0+0+0+1+2+3+4-1-1+0+1+2+3+4+4-1'', 2*(7 * WEEKDAY(sdate) + WEEKDAY(edate)) + 1, 2);
else
return -(5 * (DATEDIFF(sdate, edate) DIV 7) + MID(''+0+1+2+3+4+4+4+4+0+1+2+3+3+3+3+4+0+1+2+2+2+2+3+4+0+1+1+1+1+2+3+4+0+0+0+0+1+2+3+4-1-1+0+1+2+3+4+4-1'', 2*(7 * WEEKDAY(edate) + WEEKDAY(sdate)) + 1, 2));
end if;
-- The following works unless both start and finish date are on weekends.
-- return 5 * (DATEDIFF(edate, sdate) DIV 7) + MID(''0123444401233334012222340111123400001234000123440'', 7 * WEEKDAY(sdate) + WEEKDAY(edate) + 1, 1);
END;
En el lenguaje de la respuesta de Rodger, la tabla que creó la cadena de arriba está debajo (la única diferencia es -1 en vez de 0 para comenzar y terminar en un sábado / domingo):
| M T W T F S S
-|---------------------
M| +0 +1 +2 +3 +4 +4 +4
T| +4 +0 +1 +2 +3 +3 +3
W| +3 +4 +0 +1 +2 +2 +2
T| +2 +3 +4 +0 +1 +1 +1
F| +1 +2 +3 +4 +0 +0 +0
S| +0 +1 +2 +3 +4 -1 -1
S| +0 +1 +2 +3 +4 +4 -1
Solo para referencia futura. Ninguno de los anteriores funcionó para mí, pero una versión modificada de @Jeff Kooser:
SELECT (DATEDIFF(date_end, date_start)) -
((WEEK(date_end) - WEEK(date_start)) * 2) -
(case when weekday(date_end) = 6 then 1 else 0 end) -
(case when weekday(date_start) = 5 then 1 else 0 end) -
(SELECT COUNT(*) FROM holidays WHERE holiday>=date_start and holiday<=data_end)
Tenía este requisito y he escrito una función completa que puede calcular evitando horas de fin de semana y días festivos para un país determinado (usando una tabla separada). He puesto toda la función y los detalles en mi blog ( http://mgw.dumatics.com/mysql-function-to-calculate-elapsed-working-time/ ) junto con la explicación y el diagrama de flujo y la creación de la tabla de vacaciones, etc. . Me encantaría ponerlo aquí, pero es demasiado tiempo ...
Ejemplo de problema resuelto:
Digamos que un incidente fue registrado el "viernes 10 de junio de 2016 a las 12:00" para un sitio en el "Reino Unido" que abre de 09:00 a 16:00. Este incidente se cerró el martes 14 de junio de 2016 a las 14:00.
Para el incidente anterior, la función debe calcular la edad como 960 minutos = 16 horas = [4 horas los viernes (12:00 a 16:00) + 7 horas los lunes (09:00 a 16:00) + 5 horas los martes ( 09:00 a 14:00)]
Tendrá que usar DATEDIFF para obtener el número de días entre dos fechas en MySQL. ES DECIR:
DATEDIFF(t.date_column_1, t.date_column_2)
Pero Stephane está en lo cierto, las vacaciones son federales y regionales. Necesita crear una tabla para almacenar las fechas y hacer referencia a ellas en su cálculo.
Una función que emula el NETWORKDAYS.INTL basado en la solución de Rodger Bagnall https://.com/a/6762805/218418
DELIMITER //
DROP FUNCTION IF EXISTS NETWORKDAYS//
CREATE FUNCTION NETWORKDAYS(sd DATE, ed DATE)
RETURNS INT
LANGUAGE SQL
DETERMINISTIC
BEGIN
RETURN (5 * (DATEDIFF(ed, sd) DIV 7) + MID(''0123444401233334012222340111123400001234000123440'', 7 * WEEKDAY(sd) + WEEKDAY(ed) + 1, 1))+1;
END//
DELIMITER ;
Y para seleccionar
SELECT NETWORKDAYS(''2015-01-01 06:00:00'', ''2015-01-20 06:00:00'');
Yo uso esta solución, finalmente, por favor mira:
DROP FUNCTION IF EXISTS datediff_workdays;
CREATE FUNCTION datediff_workdays(start_date DATE, end_date DATE) RETURNS INTEGER
BEGIN
RETURN 5 * (DATEDIFF(end_date, start_date) DIV 7) + MID(''0123455501234445012333450122234501101234000123450'', 7 * WEEKDAY(start_date) + WEEKDAY(end_date) + 1, 1);
END
Below function will give you the Weekdays, Weekends, Date difference with proper results:
You can call the below function like,
select getWorkingday(''2014-04-01'',''2014-05-05'',''day_diffs'');
select getWorkingday(''2014-04-01'',''2014-05-05'',''work_days'');
select getWorkingday(''2014-04-01'',''2014-05-05'',''weekend_days'');
DROP FUNCTION IF EXISTS PREPROCESSOR.getWorkingday;
CREATE FUNCTION PREPROCESSOR.`getWorkingday`(d1 datetime,d2 datetime, retType varchar(20)) RETURNS varchar(255) CHARSET utf8
BEGIN
DECLARE dow1, dow2,daydiff,workdays, weekenddays, retdays,hourdiff INT;
declare newstrt_dt datetime;
SELECT dd.iDiff, dd.iDiff - dd.iWeekEndDays AS iWorkDays, dd.iWeekEndDays into daydiff, workdays, weekenddays
FROM (
SELECT
dd.iDiff,
((dd.iWeeks * 2) +
IF(dd.iSatDiff >= 0 AND dd.iSatDiff < dd.iDays, 1, 0) +
IF (dd.iSunDiff >= 0 AND dd.iSunDiff < dd.iDays, 1, 0)) AS iWeekEndDays
FROM (
SELECT dd.iDiff, FLOOR(dd.iDiff / 7) AS iWeeks, dd.iDiff % 7 iDays, 5 - dd.iStartDay AS iSatDiff, 6 - dd.iStartDay AS iSunDiff
FROM (
SELECT
1 + DATEDIFF(d2, d1) AS iDiff,
WEEKDAY(d1) AS iStartDay
) AS dd
) AS dd
) AS dd ;
if(retType = ''day_diffs'') then
set retdays = daydiff;
elseif(retType = ''work_days'') then
set retdays = workdays;
elseif(retType = ''weekend_days'') then
set retdays = weekenddays;
end if;
RETURN retdays;
END;
Thank You.
Vinod Cyriac.
Bangalore
DELIMITER //
DROP FUNCTION IF EXISTS NETWORKDAYS//
CREATE FUNCTION NETWORKDAYS(first_date DATE, second_date DATE)
RETURNS INT
LANGUAGE SQL
DETERMINISTIC
BEGIN
DECLARE start_date DATE;
DECLARE end_date DATE;
DECLARE diff INT;
IF (first_date < second_date) THEN
SET start_date = first_date;
SET end_date = second_date;
ELSE
SET start_date = second_date;
SET end_date = first_date;
END IF;
SET diff = DATEDIFF(end_date, start_date);
RETURN (diff + 1)
- (FLOOR(diff / 7) * 2)
- (CASE WHEN DAYNAME(start_date) = ''Sunday'' THEN 1 ELSE 0 END)
- (CASE WHEN DAYNAME(end_date) = ''Saturday'' THEN 1 ELSE 0 END);
END//
DELIMITER ;
- prueba SELECT Networkdays (''2009-12-06'', ''2009-12-13'');
SELECT 5* (DATEDIFF(u.EndDate, u.StartDate) DIV 7) + MID(''1234555512344445123333451222234511112345001234550'', 7 * WEEKDAY(u.StartDate) + WEEKDAY(u.EndDate) + 1, 1)
Esto es cuando desea considerar los siguientes casos:
1) si startdate = enddate, duration = 1 y del mismo modo ...
Calculé la cadena usando la lógica mencionada en la respuesta más votada y obtuve los resultados que necesitaba.