Pregunta Obtenga el primer día de la semana en SQL Server


Intento agrupar registros por semana, almacenando la fecha agregada como el primer día de la semana. Sin embargo, la técnica estándar que utilizo para redondear las fechas no parece funcionar correctamente con las semanas (aunque lo hace por días, meses, años, trimestres y cualquier otro período de tiempo al que lo haya aplicado).

Aquí está el SQL:

select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);

Esto regresa 2011-08-22 00:00:00.000, que es un lunes, no un domingo. Seleccionando @@datefirst devoluciones 7, que es el código para el domingo, por lo que el servidor está configurado correctamente hasta donde yo sé.

Puedo omitir esto fácilmente cambiando el código anterior a:

select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);

Pero el hecho de que tengo que hacer una excepción me hace sentir un poco incómodo. Además, me disculpo si esta es una pregunta duplicada. Encontré algunas preguntas relacionadas, pero ninguna que aborda este aspecto específicamente.


76
2017-08-23 23:50


origen


Respuestas:


Para responder por qué recibes un lunes y no un domingo:

Está agregando un número de semanas a la fecha 0. ¿Qué es la fecha 0? 1900-01-01. ¿Cuál fue el día en 1900-01-01? Lunes. Entonces, en su código, ¿cuántas semanas han pasado desde el lunes 1 de enero de 1900? Llamemos a eso [n]. De acuerdo, ahora agregue [n] semanas al lunes, 1 de enero de 1900. No debería sorprenderse que esto termine siendo un lunes. DATEADD no tiene idea de que desea agregar semanas, pero solo hasta que llegue a un domingo, solo agrega 7 días, luego agrega 7 días más, ... al igual que DATEDIFF solo reconoce los límites que se han cruzado. Por ejemplo, ambos devuelven 1, aunque algunos se quejan de que debe haber alguna lógica lógica integrada para redondear hacia arriba o hacia abajo:

SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');

Para responder a cómo obtener un domingo:

Si quieres un domingo, elige una fecha base que no sea un lunes sino un domingo. Por ejemplo:

DECLARE @dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);

Esto no se romperá si cambia su DATEFIRST configuración (o su código se está ejecutando para un usuario con una configuración diferente), siempre que desee un domingo independientemente de la configuración actual. Si quieres esas dos respuestas para jive, entonces debes usar una función que hace depende de la DATEFIRST entorno, p.

SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);

Entonces, si cambias tu DATEFIRST estableciendo hasta el lunes, martes, qué es lo que tiene, el comportamiento cambiará. Según el comportamiento que desee, puede usar una de estas funciones:

CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
(
    @d DATE
)
RETURNS DATE
AS
BEGIN
    RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @d), '19050101'));
END
GO

...o...

CREATE FUNCTION dbo.StartOfWeek2 -- always the DATEFIRST weekday
(
    @d DATE
)
RETURNS DATE
AS
BEGIN
    RETURN (SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, @d), @d));
END
GO

Ahora, tienes muchas alternativas, pero ¿cuál funciona mejor? Me sorprendería si hubiera diferencias importantes, pero recolecté todas las respuestas proporcionadas hasta el momento y las realicé en dos series de pruebas, una barata y otra cara. Medí las estadísticas del cliente porque no veo que la E / S o la memoria desempeñen un papel en el rendimiento aquí (aunque pueden entrar en juego dependiendo de cómo se usa la función). En mis pruebas, los resultados son:

Consulta de asignación "barata":

Function - client processing time / wait time on server replies / total exec time
Gandarez     - 330/2029/2359 - 0:23.6
me datefirst - 329/2123/2452 - 0:24.5
me Sunday    - 357/2158/2515 - 0:25.2
trailmax     - 364/2160/2524 - 0:25.2
Curt         - 424/2202/2626 - 0:26.3

Consulta de asignación "costosa":

Function - client processing time / wait time on server replies / total exec time
Curt         - 1003/134158/135054 - 2:15
Gandarez     -  957/142919/143876 - 2:24
me Sunday    -  932/166817/165885 - 2:47
me datefirst -  939/171698/172637 - 2:53
trailmax     -  958/173174/174132 - 2:54

Puedo transmitir los detalles de mis pruebas si lo desea, deteniéndome aquí, ya que esto ya se está volviendo bastante largo. Me sorprendió un poco ver que Curt aparecía como el más rápido en el extremo superior, dada la cantidad de cálculos y el código en línea. Tal vez haga algunas pruebas más exhaustivas y bloguee sobre ello ... si ustedes no tienen ninguna objeción al publicar sus funciones en otro lado.


119
2017-08-24 02:01



Para estos que necesitan obtener:

Lunes = 1 y Domingo = 7:

SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);

Domingo = 1 y sábado = 7:

SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);

Arriba había un ejemplo similar, pero gracias al doble "% 7" sería mucho más lento.


11
2018-02-14 16:21



Esto funciona maravillosamente para mí:

CREAR FUNCIÓN [dbo]. [StartOfWeek]
(
  @INPUTDATE DATETIME
)
DEVOLUCIONES DATETIME

COMO
EMPEZAR
  - ESTO no funciona en la función.
  - SET DATEFIRST 1 - establece que el lunes es el primer día de la semana.

  DECLARE @DOW INT - para almacenar el día de la semana
  SET @INPUTDATE = CONVERTIR (VARCHAR (10), @INPUTDATE, 111)
  SET @DOW = DATEPART (DW, @INPUTDATE)

  - Conversión mágica de lunes a 1, martes a 2, etc.
  - Independientemente de lo que piense SQL Server sobre el inicio de la semana.
  - Pero aquí tenemos el domingo marcado como 0, pero lo solucionamos más tarde.
  SET @DOW = (@DOW + @@ DATEFIRST - 1)% 7
  IF @DOW = 0 SET @DOW = 7 - corregir el domingo

  REGRESAR FECHA (DD, 1 - @ DOW, @ INPUTDATE)

FIN

4
2017-08-24 00:12



Buscado en Google esta secuencia de comandos:

create function dbo.F_START_OF_WEEK
(
    @DATE           datetime,
    -- Sun = 1, Mon = 2, Tue = 3, Wed = 4
    -- Thu = 5, Fri = 6, Sat = 7
    -- Default to Sunday
    @WEEK_START_DAY     int = 1 
)
/*
Find the fisrt date on or before @DATE that matches 
day of week of @WEEK_START_DAY.
*/
returns     datetime
as
begin
declare  @START_OF_WEEK_DATE    datetime
declare  @FIRST_BOW     datetime

-- Check for valid day of week
if @WEEK_START_DAY between 1 and 7
    begin
    -- Find first day on or after 1753/1/1 (-53690)
    -- matching day of week of @WEEK_START_DAY
    -- 1753/1/1 is earliest possible SQL Server date.
    select @FIRST_BOW = convert(datetime,-53690+((@WEEK_START_DAY+5)%7))
    -- Verify beginning of week not before 1753/1/1
    if @DATE >= @FIRST_BOW
        begin
        select @START_OF_WEEK_DATE = 
        dateadd(dd,(datediff(dd,@FIRST_BOW,@DATE)/7)*7,@FIRST_BOW)
        end
    end

return @START_OF_WEEK_DATE

end
go

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307


3
2017-08-23 23:54



Quizás necesites esto:

SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())

O

DECLARE @MYDATE DATETIME
SET @MYDATE = '2011-08-23'
SELECT DATEADD(DD, 1 - DATEPART(DW, @MYDATE), @MYDATE)

Función

CREATE FUNCTION [dbo].[GetFirstDayOfWeek]
( @pInputDate    DATETIME )
RETURNS DATETIME
BEGIN

SET @pInputDate = CONVERT(VARCHAR(10), @pInputDate, 111)
RETURN DATEADD(DD, 1 - DATEPART(DW, @pInputDate),
               @pInputDate)

END
GO

2
2017-08-24 00:01



CREAR FUNCIÓN dbo.fnFirstWorkingDayOfTheWeek
(
    @currentFecha fecha
)
DEVOLUCIONES INT
COMO
EMPEZAR
    - obtener configuración DATEFIRST
    DECLARAR @ds int = @@ DATEFIRST
    - obtener el número del día de la semana bajo la configuración DATEFIRST actual
    DECLARAR @dow int = DATEPART (dw, @ currentDate)

    DECLARAR @wd int = 1 + (((@ dow + @ ds)% 7) +5)% 7 - esto siempre devuelve Mon como 1, Tue como 2 ... Sun como 7

    REGRESAR DATEADD (dd, 1- @ wd, @ currentDate)

FIN

2
2017-12-03 13:01



Para aquellos que necesitan la respuesta en el trabajo y la creación de funciones está prohibida por su DBA, la siguiente solución funcionará:

select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart
From.....

Esto da el comienzo de esa semana. Aquí supongo que los domingos son el comienzo de las semanas. Si crees que el lunes es el comienzo, debes usar:

select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart
From.....

2
2018-03-30 16:56



Como la fecha 0 de Julian es un lunes, solo agregue el número de semanas al domingo que es el día antes de -1 Eg. seleccione dateadd (wk, datediff (wk, 0, getdate ()), - 1)


0
2017-09-24 06:46



Set DateFirst 1;

Select 
    Datepart(wk, TimeByDay) [Week]
    ,Dateadd(d,
                CASE 
                WHEN  Datepart(dw, TimeByDay) = 1 then 0
                WHEN  Datepart(dw, TimeByDay) = 2 then -1
                WHEN  Datepart(dw, TimeByDay) = 3 then -2
                WHEN  Datepart(dw, TimeByDay) = 4 then -3
                WHEN  Datepart(dw, TimeByDay) = 5 then -4
                WHEN  Datepart(dw, TimeByDay) = 6 then -5
                WHEN  Datepart(dw, TimeByDay) = 7 then -6
                END
                , TimeByDay) as StartOfWeek

from TimeByDay_Tbl

Esta es mi lógica Establezca que el primero de la semana sea el lunes, luego calcule cuál es el día de la semana en que se entrega el día, y luego usando DateAdd y Case I calcule cuál habría sido la fecha del lunes anterior de esa semana.


0
2018-05-21 04:07