powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Скалярная функция для расчета конечной даты
12 сообщений из 12, страница 1 из 1
Скалярная функция для расчета конечной даты
    #39954691
iLinks
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Написал разными способами скалярную функцию, которая принимает стартовую дату и количество рабочих дней, должна возвращать конечную дату, с учетом праздников, выходных и выходных которые стали рабочими днями.
Учет выходных дней, которые стали рабочими в таблице j_Holidays со статусом 0(там список дат у которых статус 0), учет праздников в таблице j_Holidays со статусом 1 (список дат и статус 1).
[img=]
Алгоритм вроде правильный, но почему то конечная дата выводится неправильно.
К примеру ввожу начальную дату '2020-01-01 00:00:00.000' и количество рабочих дней 1. В таблице праздники идут с 2020-01-01 по 2020-01-03 и с 2020-01-06 по 2020-01-08.
По логике конечная дата должна быть 2020-01-04, но функция выдает почему то 2020-01-09.
Вот один из способов, которые написал:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
CREATE FUNCTION [OPR].[f_GetLastWorkDay] 
(
	-- Add the parameters for the function here
	@DateStart datetime,	--Стартовая дата
	@CountWorkDay int		--Количество рабочих дней
)
RETURNS datetime
AS
BEGIN
	-- Declare the return variable here
	DECLARE @DateEnd datetime
	DECLARE @n int = 0
	DECLARE @next bit
	DECLARE @ex0 int
	DECLARE @ex1 int
	
	set @DateEnd = @DateStart

	--Циклом добавляем дни
	while (@n<@CountWorkDay)
	BEGIN
		set @n=@n+1
		set @next = 0
		set @DateEnd = DATEADD(day,1,@DateEnd)
		while (@next = 0)
		BEGIN
			IF(DATEPART(weekday, @DateEnd) = 7
			or DATEPART(weekday, @DateEnd) = 1)
			begin
				set @ex0 = (select COUNT(*) from dbo.j_Holidays j_H where  j_H.Date_Holiday in(@DateEnd) AND j_H.Status_Holiday = 0)
				if (@ex0=1)
				begin
					break
				end
				else
				begin
					set @DateEnd = DATEADD(day,1,@DateEnd)
					continue
				end
			end
			ELSE
			begin
				set @ex1 = (select COUNT(*) from dbo.j_Holidays j_H where j_H.Date_Holiday in(@DateEnd) AND j_H.Status_Holiday = 1)
				if (@ex1=1)
				begin
					set @DateEnd = DATEADD(day,1,@DateEnd)
					continue
				end
				else
				begin
					break
				end
			end		
		END
	END
	-- Return the result of the function
	RETURN @DateEnd
END
GO
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954696
Гавриленко Сергей Алексеевич
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Написали ужасного монстра - теперь отладкой займитесь.

Еще я бы уточнил, какой день по вашим настройкам сервер считает первым в неделе.
Явной установки, что это понедельник, в коде не наблюдается.
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954701
iLinks
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Гавриленко Сергей Алексеевич,

Код: sql
1.
IF(DATEPART(weekday, @DateEnd) = 7 or DATEPART(weekday, @DateEnd) = 1)


Это условие на субботу и воскресенье
Да вроде не монстра. Могу алгоритм нарисовать, если это как то поможет. А помощь нужна :)
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954705
Гавриленко Сергей Алексеевич
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
iLinks,

Еще раз. В разных странах неделя начинается в разные дни недели и в разных странах понедельник будет иметь разный порядковый номер. И суббота с воскресеньем тоже.
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954718
iLinks
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Гавриленко Сергей Алексеевич,

...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954720
uaggster
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вообще-то, правильным вариантом будет:
1. Сходить вот сюда: http://data.gov.ru/opendata/resource/8ba5011a-233e-4e01-a1d2-ff5598d0f34f#2/0.0/0.0
2. Скачать и распарсить CSV в нормальную таблицу, лучше вида: Дата - признак рабочий/нерабочий - день недели.
3. Для дней за пределами календаря, например - в будущем или до 1994 года - проставить признак нерабочий день для субботы и воскресенья (например).
4. Считать количество рабочих и нерабочих дней - прямым подсчетом, в интервале дат.
5. Раз в год обновлять табличку.
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954801
iap
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
iLinks
Гавриленко Сергей Алексеевич,

Выполните в начале SET DATEFIRST 4;
Всё ещё как на вашем рисунке?
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954805
iLinks
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
iap,

Я не могу выполнить
Код: sql
1.
SET DATEFIRST

так как это рабочий сервер и там многие процедуры из за этого могут полететь.
Но первый день недели на сервере - это воскресенье.
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954815
Фотография alexeyvg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
iLinks
Да вроде не монстра. Могу алгоритм нарисовать, если это как то поможет. А помощь нужна :)
Ага, одно это
Код: sql
1.
2.
set @ex1 = (select COUNT(*) from #j_Holidays j_H where j_H.Date_Holiday in(@DateEnd) AND j_H.Status_Holiday = 1)
if (@ex1=1)

Вместо if exists чего стоит

iLinks
Могу алгоритм нарисовать, если это как то поможет. А помощь нужна :)
Что в таблице Date_Holiday, и что означает поле Status_Holiday?

Если не хотите сделать нормально, как написал uaggster, то хотя бы сделайте из вашего монстра один запрос, по типу
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
select @DateEnd = dt
from (
	select top (@CountWorkDay) dt, row_number() over(order by dt desc) as N
	from (
		select dateadd(dd, N, @DateStart) as dt
		from (
			select top (@CountWorkDay*2 + 30) (row_number() over(order by getdate())) - 1 as N from sysobjects o1 cross join sysobjects o2
		) t
	) t
	where DATEPART(weekday, dt) not in (1,7)
) t
where N = 1
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954817
invm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
iLinks,

Ваша задача решается одним запросом и без заумных циклов.
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
declare @DateStart date = getdate(), @DaysCount int = 23;

with t as
(
 select
  @DateStart as d, case when (datepart(dw, @DateStart) + @@datefirst - 2) % 7 + 1 in (6, 7) then 0 else 1 end as cnt
 where
  @DaysCount > 0

 union all

 select
  a.d, t.cnt + case when (datepart(dw, a.d) + @@datefirst - 2) % 7 + 1 in (6, 7) then 0 else 1 end
 from
  t cross apply
  (select dateadd(day, 1, t.d)) a(d)
 where
  t.cnt < @DaysCount
)
select
 max(d), datediff(day, @DateStart, max(d))
from
 t
option
 (maxrecursion 0);


Таблицу праздников добавите самостоятельно.

И вместо скалярной функции сделать инлайновую
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954822
iap
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
iLinks
iap,

Я не могу выполнить
Код: sql
1.
SET DATEFIRST


так как это рабочий сервер и там многие процедуры из за этого могут полететь.
Но первый день недели на сервере - это воскресенье.
SET действует только в текущей сессии.
Никто, кроме вас, не заметит.
...
Рейтинг: 0 / 0
Скалярная функция для расчета конечной даты
    #39954866
Фотография Ennor Tiegael
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
iap,

Еще лучше будет так:
Код: sql
1.
set language russian;
...
Рейтинг: 0 / 0
12 сообщений из 12, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Скалярная функция для расчета конечной даты
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]