Как мне взять день в году и "разбить его" на недели года в Microsoft SQL?Используется в производственных сценариях с учетом требований к материалам
-
03-07-2019 - |
Вопрос
Мне нужно создать отчет о совокупных потребностях, который учитывает объем предложения и спроса на товар на складе с даты начала и далее и распределяет его по разным неделям года, чтобы специалисты по планированию материалов знали, когда им понадобится товар и достаточно ли у них запасов на складе на этот момент.
В качестве примера, сегодняшняя дата (дата отчета) - 27.08.08.Первый шаг - найти дату для понедельника недели, на которую приходится дата отчета.В этом случае понедельник будет 25.08.08.Это становится первым днем первого ведра.Все транзакции, которые приходятся на предыдущую неделю, присваиваются неделе №0 и будут суммированы как начальный баланс для отчета.Остальные сегменты рассчитываются исходя из этой точки.Для восьмого сегмента дата окончания отсутствует, поэтому любые транзакции после этой даты начала 8-го сегмента считаются неделей №8.
НЕДЕЛЯ# ДАТА НАЧАЛА ДАТА ОКОНЧАНИЯ
0......Нет..........8/24/08
1.......8/25/08.......8/31/08
2.......9/1/08.........9/7/08
3.......9/8/08.........9/14/08
4.......9/15/08.......9/21/08
5.......9/22/08.......9/28/08
6.......9/29/08.......10/5/08
7.......10/06/08.....10/12/08
8.......10/13/08...... Нет
Как мне получить номер недели, дату начала, дату окончания для данной даты?
Решение
Вы можете получить понедельник на любую заданную дату недели следующим образом:
DATEADD(d, 1 - DATEPART(dw, @date), @date)
и вы можете написать хранимую процедуру со следующим телом
-- 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
Когда я прохожу мимо
@date = '20080827'
в результате этой процедуры я получаю следующее
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
Другие советы
Я всегда считал, что проще всего и эффективнее (для SQL Server) создавать таблицу с одной строкой для каждой недели в будущем на горизонте вашего домена;и присоединитесь к этому (с помощью "ГДЕ GETDATE() >= MONDATE И НЕ СУЩЕСТВУЕТ (ВЫБЕРИТЕ 1 ИЗ таблицы, ГДЕ MONDATE < GETDATE())".
Все, что вы попытаетесь сделать с UDF, будет намного менее эффективным, и я нахожу его более сложным в использовании.
--SQL устанавливает первый день недели как воскресенье, и для наших целей мы хотим, чтобы это был понедельник.
--Эта команда делает это.
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
Проблема, которую я вижу при подходе "одно ведро за раз", заключается в том, что его трудно масштабировать,
Если вы присоединитесь к определяемой пользователем функции, вы получите более высокую производительность, вы могли бы использовать это отправная точка
Почему бы не использовать комбинацию DATEPART (год, столбец даты) и DATEPART (неделя, столбец даты) и сгруппировать по этим значениям.Это работает, если неделя в ЧАСТЬ ДАТЫ выравнивается по понедельникам в соответствии с требованиями стандарта ISO 8601.В общих чертах:
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...