¿Cómo tomo un día del año y lo 'agrupo' en semanas del año en Microsoft SQL? Se utiliza en escenarios de fabricación para requisitos de materiales.
-
03-07-2019 - |
Pregunta
Tengo la necesidad de crear un informe de requisitos brutos que tome la cantidad de oferta y demanda de un artículo en el inventario desde una fecha de inicio en adelante y lo 'agrupe' en diferentes semanas del año para que los planificadores de materiales sepan cuándo necesitarán un artículo y si tienen suficiente stock en inventario en ese momento.
Como ejemplo, hoy & # 8217; la fecha (fecha del informe) es el 8/27/08. El primer paso es encontrar la fecha para el lunes de la semana en que cae la fecha del informe. En este caso, el lunes sería el 25/08/08. Este se convierte en el primer día del primer cubo. Todas las transacciones que caen antes de eso se asignan a la semana # 0 y se resumirán como el saldo inicial del informe. Los cubos restantes se calculan a partir de ese punto. Para el octavo segmento, no hay fecha de finalización, por lo que cualquier transacción posterior a esa fecha de inicio del octavo segmento se considera la semana # 8.
SEMANA # FECHA DE INICIO FECHA DE FINAL
0 ....... Ninguno .......... 24/08/08
1 ....... 25/08/08 ....... 31/08/08
2 ....... 9/1/08 ......... 9/7/08
3 ....... 8/09/08 ......... 14/09/08
4 ....... 15/09/08 ....... 21/09/08
5 ....... 22/09/08 ....... 28/09/08
6 ....... 29/09/08 ....... 5/10/08
7 ....... 10/06/08 ..... 10/12/08
8 ....... 13/10/08 ...... Ninguno
¿Cómo obtengo el número de semana, la fecha de inicio y la fecha de finalización para una fecha determinada?
Solución
Puede obtener el lunes para cualquier fecha en una semana como:
DATEADD(d, 1 - DATEPART(dw, @date), @date)
y puede escribir un procedimiento almacenado con el siguiente cuerpo
-- find Monday at that week
DECLARE @currentDate SMALLDATETIME
SELECT @currentDate = DATEADD(d, 1 - DATEPART(dw, @date), @date)
-- create a table and insert the first record
DECLARE @weekTable TABLE (Id INT, StartDate SMALLDATETIME, EndDate SMALLDATETIME)
INSERT INTO @weekTable VALUES (0, NULL, @currentDate)
-- increment the date
SELECT @currentDate = DATEADD(d, 1, @currentDate)
-- iterate for 7 more weeks
DECLARE @id INT
SET @id = 1
WHILE @id < 8
BEGIN
INSERT INTO @weekTable VALUES (@id, @currentDate, DATEADD(d, 6, @currentDate))
SELECT @currentDate = DATEADD(ww, 1, @currentDate)
SET @id = @id + 1
END
-- add the last record
INSERT INTO @weekTable VALUES (8, @currentDate, NULL)
-- select the values
SELECT Id 'Week #', StartDate 'Start Date', EndDate 'End Date'
FROM @weekTable
Cuando paso
@date = '20080827'
a este procedimiento, obtengo lo siguiente
Week # Start Date End Date
0 NULL 2008-08-24 00:00:00
1 2008-08-25 00:00:00 2008-08-31 00:00:00
2 2008-09-01 00:00:00 2008-09-07 00:00:00
3 2008-09-08 00:00:00 2008-09-14 00:00:00
4 2008-09-15 00:00:00 2008-09-21 00:00:00
5 2008-09-22 00:00:00 2008-09-28 00:00:00
6 2008-09-29 00:00:00 2008-10-05 00:00:00
7 2008-10-06 00:00:00 2008-10-12 00:00:00
8 2008-10-13 00:00:00 NULL
Otros consejos
Siempre he encontrado que es más fácil y más eficiente (para SQL Server) construir una tabla con una fila para cada semana en el futuro a través de su horizonte de dominio; y únete a eso (con un " WHERE GETDATE () > = MONDATE AND NOT EXISTS (SELECT 1 FROM table WHERE MONDATE < GETDATE ()) ".
Cualquier cosa que intente hacer con UDF será mucho menos eficiente y encuentro más difícil de usar.
--SQL establece el primer día de la semana como domingo y para nuestros propósitos queremos que sea lunes.
- Este comando hace eso.
SET DATEFIRST 1
DECLARE
@ReportDate DATETIME,
@Weekday INTEGER,
@NumDaysToMonday INTEGER,
@MondayStartPoint DATETIME,
@MondayStartPointWeek INTEGER,
@DateToProcess DATETIME,
@DateToProcessWeek INTEGER,
@Bucket VARCHAR(50),
@DaysDifference INTEGER,
@BucketNumber INTEGER,
@NumDaysToMondayOfDateToProcess INTEGER,
@WeekdayOfDateToProcess INTEGER,
@MondayOfDateToProcess DATETIME,
@SundayOfDateToProcess DATETIME
SET @ReportDate = '2009-01-01'
print @ReportDate
SET @DateToProcess = '2009-01-26'
--print @DateToProcess
SET @Weekday = (select DATEPART ( dw , @ReportDate ))
--print @Weekday
--print DATENAME(dw, @ReportDate)
SET @NumDaysToMonday =
(SELECT
CASE
WHEN @Weekday = 1 THEN 0
WHEN @Weekday = 2 THEN 1
WHEN @Weekday = 3 THEN 2
WHEN @Weekday = 4 THEN 3
WHEN @Weekday = 5 THEN 4
WHEN @Weekday = 6 THEN 5
WHEN @Weekday = 7 THEN 6
END)
--print @NumDaysToMonday
SET @MondayStartPoint = (SELECT DATEADD (d , -1*@NumDaysToMonday, @ReportDate))
--print @MondayStartPoint
SET @DaysDifference = DATEDIFF ( dd , @MondayStartPoint , @DateToProcess )
--PRINT @DaysDifference
SET @BucketNumber = @DaysDifference/7
--print @BucketNumber
----Calculate the start and end dates of this bucket------
PRINT 'Start Of New Calc'
print @DateToProcess
SET @WeekdayOfDateToProcess = (select DATEPART ( dw , @DateToProcess ))
print @WeekdayOfDateToProcess
SET @NumDaysToMondayOfDateToProcess=
(SELECT
CASE
WHEN @WeekdayOfDateToProcess = 1 THEN 0
WHEN @WeekdayOfDateToProcess = 2 THEN 1
WHEN @WeekdayOfDateToProcess = 3 THEN 2
WHEN @WeekdayOfDateToProcess = 4 THEN 3
WHEN @WeekdayOfDateToProcess = 5 THEN 4
WHEN @WeekdayOfDateToProcess = 6 THEN 5
WHEN @WeekdayOfDateToProcess = 7 THEN 6
END)
print @NumDaysToMondayOfDateToProcess
SET @MondayOfDateToProcess = (SELECT DATEADD (d , -1*@NumDaysToMondayOfDateToProcess, @DateToProcess))
print @MondayOfDateToProcess ---This is the start week
SET @SundayOfDateToProcess = (SELECT DATEADD (d , 6, @MondayOfDateToProcess))
PRINT @SundayOfDateToProcess
El problema que veo con el enfoque de un cubo a la vez es que es difícil hacerlo escalar,
Si se une a una función definida por el usuario obtendrá un mejor rendimiento, puede usar este aa punto de partida
¿Por qué no utilizar una combinación de DATEPART (año, columna de fecha) y DATEPART (semana, columna de fecha) y agrupar por estos valores. Esto funciona si la semana en DATEPART está alineada los lunes como ISO 8601 requiere En resumen:
SELECT DATEPART(year, date_column) AS yyyy,
DATEPART(week, date_column) AS ww,
...other material as required...
FROM SomeTableOrOther
WHERE ...appropriate filters...
GROUP BY yyyy, ww -- ...and other columns as necessary...