none
Calcular fechas entre fechas RRS feed

  • Pregunta

  • Hola chicos saludos ante todo, tengo la sigueinte situacion, estoy haciendo un soft en C# y quiero generar unos graficos estadisticos los cuales los extraigo de un procedimiento almacenado en SQL Server, es aca la cosa, necesito extraer entre dos fechas no solo la cantidad de semanas sino cada rango de estas semanas, y al mismo tiempo que me extraiga de la tabla los valores de cada una de estas semanas, no se si me entienden deja poner un ejemplo, 20170801 esto es miercoles de agosto, hasta el domingo consideraria una semana aunque en realidad solo tiene 5 dias, hasta el 20 que cae domingo serian 3 semanas esto sale con el DATEDIFF, ahora lo que necesito son los rangos que me diera desde el (01-08-2017 hasta 06-08-2017) semana 1, (07-08-2017 hasta 13-08-2017) semana 2, (14-08-2017 hasta 20-08-2017) Semana 3, y que e devuelva los rangos de fechas, asi poder extraer entre estos rangos los valores cruzados con mi tabla para las estadisticas, asi sumaria los valores semanales y diera el resumen por semana.

    Diganme chicos seria posible hacer esto??

    Muchas gracias


    Ing. Dariel Alvarez Perez

    martes, 29 de agosto de 2017 12:14

Respuestas

  • Dariel,

    Si es posible.  Al parecer la semana la defines de Lunes - Domingo por lo que para cada dia en el rango calcularias el Lunes anterior inclusive (si es Lunes pues ahi se queda).

    Para calcular un dia anterior determinado, puedes usar una fecha que sea ese dia como ancla y a partir de ahi hacer el calculo. Veamos esto adaptado a tu ejemplo:

    semana 1: 07-08-2017 hasta 13-08-2017 -> Lunes anterior 07-08-2017
    semana 2: 14-08-2017 hasta 20-08-2017 -> Lunes anterior 14-08-2017

    Escojamos un dia cualquiera de ellos, por ejemplo Miercoles, 09-08-2017 y usando una fecha ancla que haya sido Lunes (19000101) entonces caclulamos la diferencia en dias entre estas, la dividimos por 7 (division entera - no restos para no contar dias intermedios), este valor lo multiplicamos por 7 (dias). Si le sumamos este valor a la fecha ancla pues daria el Lunes anterior inclusive.

    Ejemplo:

    SELECT
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170807') / 7 * 7, '19000101') AS col1,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170808') / 7 * 7, '19000101') AS col2,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170809') / 7 * 7, '19000101') AS col3,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170810') / 7 * 7, '19000101') AS col4,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170811') / 7 * 7, '19000101') AS col5,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170812') / 7 * 7, '19000101') AS col6,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170813') / 7 * 7, '19000101') AS col7;
    GO

    Asi que partiendo de un rango de fechas, calculamos el Lunes anterior para cada fecha y los grupos de una misma semana tendran igual valor por lo que el resto seria calcular la fecha minima y maxima de cada grupo. El rango de dias lo puedes generar usando una CTE recursiva o mediante una tabla auxiliar de numeros.

    Ejemplo:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -- Itzik's VATN
    CREATE FUNCTION [dbo].[ufn_GetNums](@low AS bigint, @high AS bigint) 
    RETURNS table
    AS
    RETURN (
    WITH
    L0 AS (SELECT c FROM (VALUES(1),(1)) AS D(c)),
    L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
    L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
    L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
    L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
    L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
    Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM L5)
    SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
    FROM Nums
    ORDER BY rownum
    );
    GO
    DECLARE @sdt date = '20170801', @edt date = '20170820';
    
    WITH Dates AS (
    SELECT
    	DATEADD(DAY, n, @sdt) AS dt
    FROM
    	dbo.ufn_GetNums(0, DATEDIFF(DAY, @sdt, @edt)) AS T
    )
    SELECT
    	G.prv_Lunes,
    	ROW_NUMBER() OVER(ORDER BY G.prv_Lunes) AS rn,
    	MIN(D.dt) AS primer_dia,
    	MAX(D.dt) AS ultimo_dia
    FROM
    	Dates AS D
    	CROSS APPLY
        (VALUES (DATEADD(DAY, DATEDIFF(DAY, '19000101', D.dt) / 7 * 7, '19000101'))) AS G(prv_Lunes)
    GROUP BY
    	G.prv_Lunes
    ORDER BY
    	G.prv_Lunes;
    GO
    
    /*
    
    prv_Lunes	        rn	primer_dia	ultimo_dia
    2017-07-31 00:00:00.000	1	2017-08-01	2017-08-06
    2017-08-07 00:00:00.000	2	2017-08-07	2017-08-13
    2017-08-14 00:00:00.000	3	2017-08-14	2017-08-20
    
    */


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas


    martes, 29 de agosto de 2017 13:08
  • Hola, para mi la forma mas sencilla es usando la funcion DATEPART y DATEDIFF con la "week" como punto de partida.. con esta simple consulta obtienes el número de semana del mes en curso.

    SELECT     DATEPART(week, '20170828') - DATEDIFF(week,'20170101','20170801') as Semana
    

    Ahora puedes jugar pasando tu fecha y una pequeña función que te devuelva el valor, 1,2,3,4 o 5 ... o al vuelo, y luego un OVER (PARTITION BY Semana), etc..


    JM Claudio Dba/Consultor SQL/BI Pasiona - Spain

    • Propuesto como respuesta JM Claudio martes, 29 de agosto de 2017 13:24
    • Marcado como respuesta Dariel Alvarez Perez martes, 29 de agosto de 2017 13:46
    martes, 29 de agosto de 2017 13:17

Todas las respuestas

  • Dariel,

    Si es posible.  Al parecer la semana la defines de Lunes - Domingo por lo que para cada dia en el rango calcularias el Lunes anterior inclusive (si es Lunes pues ahi se queda).

    Para calcular un dia anterior determinado, puedes usar una fecha que sea ese dia como ancla y a partir de ahi hacer el calculo. Veamos esto adaptado a tu ejemplo:

    semana 1: 07-08-2017 hasta 13-08-2017 -> Lunes anterior 07-08-2017
    semana 2: 14-08-2017 hasta 20-08-2017 -> Lunes anterior 14-08-2017

    Escojamos un dia cualquiera de ellos, por ejemplo Miercoles, 09-08-2017 y usando una fecha ancla que haya sido Lunes (19000101) entonces caclulamos la diferencia en dias entre estas, la dividimos por 7 (division entera - no restos para no contar dias intermedios), este valor lo multiplicamos por 7 (dias). Si le sumamos este valor a la fecha ancla pues daria el Lunes anterior inclusive.

    Ejemplo:

    SELECT
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170807') / 7 * 7, '19000101') AS col1,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170808') / 7 * 7, '19000101') AS col2,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170809') / 7 * 7, '19000101') AS col3,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170810') / 7 * 7, '19000101') AS col4,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170811') / 7 * 7, '19000101') AS col5,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170812') / 7 * 7, '19000101') AS col6,
    	DATEADD(DAY, DATEDIFF(DAY, '19000101', '20170813') / 7 * 7, '19000101') AS col7;
    GO

    Asi que partiendo de un rango de fechas, calculamos el Lunes anterior para cada fecha y los grupos de una misma semana tendran igual valor por lo que el resto seria calcular la fecha minima y maxima de cada grupo. El rango de dias lo puedes generar usando una CTE recursiva o mediante una tabla auxiliar de numeros.

    Ejemplo:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -- Itzik's VATN
    CREATE FUNCTION [dbo].[ufn_GetNums](@low AS bigint, @high AS bigint) 
    RETURNS table
    AS
    RETURN (
    WITH
    L0 AS (SELECT c FROM (VALUES(1),(1)) AS D(c)),
    L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
    L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
    L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
    L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
    L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
    Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM L5)
    SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
    FROM Nums
    ORDER BY rownum
    );
    GO
    DECLARE @sdt date = '20170801', @edt date = '20170820';
    
    WITH Dates AS (
    SELECT
    	DATEADD(DAY, n, @sdt) AS dt
    FROM
    	dbo.ufn_GetNums(0, DATEDIFF(DAY, @sdt, @edt)) AS T
    )
    SELECT
    	G.prv_Lunes,
    	ROW_NUMBER() OVER(ORDER BY G.prv_Lunes) AS rn,
    	MIN(D.dt) AS primer_dia,
    	MAX(D.dt) AS ultimo_dia
    FROM
    	Dates AS D
    	CROSS APPLY
        (VALUES (DATEADD(DAY, DATEDIFF(DAY, '19000101', D.dt) / 7 * 7, '19000101'))) AS G(prv_Lunes)
    GROUP BY
    	G.prv_Lunes
    ORDER BY
    	G.prv_Lunes;
    GO
    
    /*
    
    prv_Lunes	        rn	primer_dia	ultimo_dia
    2017-07-31 00:00:00.000	1	2017-08-01	2017-08-06
    2017-08-07 00:00:00.000	2	2017-08-07	2017-08-13
    2017-08-14 00:00:00.000	3	2017-08-14	2017-08-20
    
    */


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas


    martes, 29 de agosto de 2017 13:08
  • Hola, para mi la forma mas sencilla es usando la funcion DATEPART y DATEDIFF con la "week" como punto de partida.. con esta simple consulta obtienes el número de semana del mes en curso.

    SELECT     DATEPART(week, '20170828') - DATEDIFF(week,'20170101','20170801') as Semana
    

    Ahora puedes jugar pasando tu fecha y una pequeña función que te devuelva el valor, 1,2,3,4 o 5 ... o al vuelo, y luego un OVER (PARTITION BY Semana), etc..


    JM Claudio Dba/Consultor SQL/BI Pasiona - Spain

    • Propuesto como respuesta JM Claudio martes, 29 de agosto de 2017 13:24
    • Marcado como respuesta Dariel Alvarez Perez martes, 29 de agosto de 2017 13:46
    martes, 29 de agosto de 2017 13:17
  • Muchas gracias dejame estudiar lo que me dices gracias por todo de nuevo.

    Ing. Dariel Alvarez Perez

    martes, 29 de agosto de 2017 13:46
  • Gracias hermano agradecido voy a probar a ver que tal.

    Ing. Dariel Alvarez Perez

    martes, 29 de agosto de 2017 13:46
  • Hola, si necesitas alguna aclaración o ayuda mas no dudes en consultarnos.

    En principio puede darte problemas si tienes definido el inicio de semana en Domingo(opción por defecto en inglés) para ello tan sencillo como añadir " set datefirst 1; " antes de la consulta...

    y en el DATEDIFF tienes primero el primer día del año de la fecha que estás consultando en DATEPART y luego el primer día del mes de dicha fecha.. para obtener las semanas del año que tienes que restar a la actual.. Espero que te resulte sencillo de implementar...


    JM Claudio Dba/Consultor SQL/BI Pasiona - Spain

    martes, 29 de agosto de 2017 13:54