2011-08-23 114 views
74

Estoy intentando 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).Obtenga el primer día de la semana en SQL Server

Aquí es el SQL:

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

Esto devuelve 2011-08-22 00:00:00.000, que es un lunes, no un domingo. Al seleccionar @@datefirst se devuelve 7, que es el código del domingo, por lo que el servidor está configurado correctamente hasta donde yo sé.

que puede pasar por alto esta bastante fácilmente cambiando el código anterior para:

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

Pero el hecho de que tengo que hacer una excepción tal me hace 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.

+8

'(@@ DATEFIRST + DATEPART (DW, @SomeDate))% 7' permanece constante independientemente de la configuración' @@ datefirst', creo. Con Monday = 2. –

Respuesta

116

de responder por qué usted está recibiendo un lunes y no un domingo:

Has añadido varias 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 quiere agregar semanas, pero solo hasta que llegue a un domingo, solo agregue 7 días, luego agregue 7 días más, ... al igual que DATEDIFF, solo reconoce los límites que se han cruzado. Por ejemplo, éstos tanto volver 1, a pesar de que algunas personas se quejan de que no debe haber cierta lógica sensata incorporado 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 conseguir un Domingo:

Si quieres un domingo, luego 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 se cambia la configuración de DATEFIRST (o su código se ejecuta para un usuario con una configuración diferente) - siempre y cuando usted todavía desea un domingo, independientemente del ajuste actual.Si quiere esas dos respuestas para jive, entonces debe usar una función que hace según la configuración DATEFIRST, p.

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

Así que si cambia la configuración de DATEFIRST a lunes, martes, lo que tiene, el comportamiento cambiará. En función de la conducta que desea, se puede utilizar 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, usted tiene un montón de 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 cesión "barato":

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 

"caro" consulta asignación:

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 

que pueden retransmitir los detalles de mis pruebas, si lo desea - parar 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.

+0

Por lo tanto, si considero que mis semanas comienzan el domingo y finalizan el sábado, puedo obtener el último día de la semana para cualquier fecha como esta: SELECCIONAR FECHA FECHA (semana, DATEDIFF (semana, '19041231', @d) , '19041231') – Baodad

3

buscado en Google este script:

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

2

Tal vez usted necesita 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 
+5

'DATEPART (DW' depende de' @@ datefirst' –

+0

@Martin Smith bienintencionado! – Gandarez

+0

Me gusta la simplicidad de este. Parece funcionar bastante bien para grandes conjuntos de datos también. –

4

Esto funciona de maravilla para mí:

 
CREATE FUNCTION [dbo].[StartOfWeek] 
(
    @INPUTDATE DATETIME 
) 
RETURNS DATETIME 

AS 
BEGIN 
    -- THIS does not work in function. 
    -- SET DATEFIRST 1 -- set monday to be the first day of week. 

    DECLARE @DOW INT -- to store day of week 
    SET @INPUTDATE = CONVERT(VARCHAR(10), @INPUTDATE, 111) 
    SET @DOW = DATEPART(DW, @INPUTDATE) 

    -- Magic convertion of monday to 1, tuesday to 2, etc. 
    -- irrespect what SQL server thinks about start of the week. 
    -- But here we have sunday marked as 0, but we fix this later. 
    SET @DOW = (@DOW + @@DATEFIRST - 1) %7 
    IF @DOW = 0 SET @DOW = 7 -- fix for sunday 

    RETURN DATEADD(DD, 1 - @DOW,@INPUTDATE) 

END 
+0

Esto parece regresar el lunes dada la fecha de hoy, no el domingo. El OP ya tiene una función que regresa el lunes, él quiere que regrese el domingo. :-) –

+0

d ¡Oh! Debo leer preguntas más cuidadosas la próxima vez. Sin embargo, mi solución puede ajustarse fácilmente, si aún se requiere. Parece que OP está contento con la respuesta aceptada de todos modos -) – trailmax

+0

Esta es la solución correcta en mi máquina, ya que para mí: DATEADD (ww, DATEDIFF (ww, 0, CONVERT (FECHA, '2017-10-8')), 0) devuelve 2017-10-9! –

-1

No tengo ningún problema con ninguna de las respuestas dadas aquí, sin embargo, creo que la mía es mucho más sencilla de implementar y comprender. No he realizado ninguna prueba de rendimiento, pero debería ser negligente.

Así que obtuve mi respuesta del hecho de que las fechas se almacenan en el servidor SQL como enteros (estoy hablando solo del componente de fecha). Si no me crees, prueba con SELECT CONVERT (INT, GETDATE()) y viceversa.

Ahora sabiendo esto, puede hacer algunas ecuaciones matemáticas geniales. Es posible que puedas encontrar uno mejor, pero aquí está el mío.

/* 
TAKEN FROM http://msdn.microsoft.com/en-us/library/ms181598.aspx 
First day of the week is 
1 -- Monday 
2 -- Tuesday 
3 -- Wednesday 
4 -- Thursday 
5 -- Friday 
6 -- Saturday 
7 (default, U.S. English) -- Sunday 
*/ 

--Offset is required to compensate for the fact that my @@DATEFIRST setting is 7, the default. 
DECLARE @offSet int, @testDate datetime 
SELECT @offSet = 1, @testDate = GETDATE() 

SELECT CONVERT(DATETIME, CONVERT(INT, @testDate) - (DATEPART(WEEKDAY, @testDate) - @offSet)) 
+1

Me parece que esto no funciona para mí. Mi '@@ DATEFIRST' también es 7, pero si su' @ testDate' es el comienzo de la semana, esto devuelve una fecha que es el día anterior. – row1

-3

tal vez soy más de la simplificación de aquí, y que puede ser el caso, pero esto parece que funciona para mí. No he encontrado ningún problema aún ...

CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7 - 7) as 'FirstDayOfWeek' 
CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7) as 'LastDayOfWeek' 
+0

Puede obtener diferentes respuestas aquí si prueba diferentes configuraciones para 'SET DATEFIRST'. –

+0

Solo CONFIGURAR DATEFIRST 1, problema resuelto. – mark

+5

Bueno, no voté, pero su respuesta no mencionó 'DATEFIRST' en absoluto (durante tres años y medio ahora), y todavía no lo hace. Y debe * también * evitar formatos regionales como 'm/d/y', incluso en escenarios en los que myd son los mismos. –

-1

Tuve un problema similar. Dada una fecha, quería obtener la fecha del lunes de esa semana.

Utilicé la siguiente lógica: encuentre el número de día en la semana en el rango de 0-6, luego reste eso de la fecha de inicio.

utilicé: DATEADD (día, - (DATEPART (de lunes a viernes,) + 5)% 7,)

Desde DATEPRRT (de lunes a viernes,) devuelve 1 = Sundaye ... 7 = Sábado, DATEPART (de lunes a viernes ,) + 5)% 7 devuelve 0 = lunes ... 6 = domingo.

Restando este número de días de la fecha original da el lunes anterior. La misma técnica podría usarse para cualquier día de inicio de la semana.

2
 
CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek 
(
    @currentDate date 
) 
RETURNS INT 
AS 
BEGIN 
    -- get DATEFIRST setting 
    DECLARE @ds int = @@DATEFIRST 
    -- get week day number under current DATEFIRST setting 
    DECLARE @dow int = DATEPART(dw,@currentDate) 

    DECLARE @wd int = 1+(((@[email protected]) % 7)+5) % 7 -- this is always return Mon as 1,Tue as 2 ... Sun as 7 

    RETURN DATEADD(dd,[email protected],@currentDate) 

END 
+0

Esta es la única función que funcionó para mí en SQL Server 2005. Gracias – Fandango68

+0

@ Fernando68 ¿Puede explicar cómo otras * soluciones * no funcionaron? –

+0

@AaronBertrand Lo siento, no recuerdo, pero creo que me estaba centrando en una respuesta rápida y probé la suya, pero por alguna razón no funcionó para mí. – Fandango68

10

Para estos que necesita para obtener:

Lunes = 1 y domingo = 7:

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

Domingo = 1 y el sábado = 7:

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

Por encima de allí fue un ejemplo similar, pero gracias al doble "% 7" sería mucho más lento.

+0

Esto también funciona muy bien para obtener el número del día desde el comienzo de la semana en domingos o lunes. Gracias – Fandango68

+0

Alternativamente 'select (datediff (dd, 5, cal.D_DATE)% 7 + 1)' y 'select (datediff (dd, 6, cal.D_DATE)% 7 + 1)' – vasja

0

Dado que la fecha juliana 0 es un lunes, solo agregue el número de semanas al domingo que es el día antes de -1 Ej. select dateadd (wk, datediff (wk, 0, getdate()), - 1)

-1

Encontré esto simple y útil. Funciona incluso si el primer día de la semana es domingo o lunes.

DECLARE COMO @BaseDate Fecha

SET @BaseDate = GETDATE() @FisrtDOW

declarar como Fecha

SELECT @FirstDOW = DATEADD (d, DATEPART (lunes a viernes, fechabase @) * - 1 + 1, @BaseDate)

Cuestiones relacionadas