Гость
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Подскажите как сделать отбор / 10 сообщений из 10, страница 1 из 1
11.02.2022, 11:21
    #40133412
alexandrDr87
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
Есть таблица со следующими данными. Время открытия и закрытия может быть как в одном месяце, так и в разных.


INCIDENT | OPEN_TIME | CLOSE_TIME
===============================================
IN123456 | 2022-01-03 08:41:22.000 | 2022-03-10 09:40:27.000


Мне необходимо отобрать такие записи в другую таблицу, где они бы разделились следующим образом:

INCIDENT | OPEN_TIME | CLOSE_TIME
===============================================
IN123456 | 2022-01-03 08:41:22.000 | 2022-01-31 23:59:00.000

IN123456 | 2022-02-01 00:00:00.000 | 2022-02-28 23:59:00.000

IN123456 | 2022-03-01 00:00:00.000 | 2022-03-10 09:40:27.000

То есть чтоб первоначальная запись разбилась на промежутки по месяцам и последний промежуток заканчивался как раз датой CLOSE_TIME первоначальной таблицы
...
Рейтинг: 0 / 0
11.02.2022, 11:29
    #40133420
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
alexandrDr87,

составьте календарь последних дней месяца, выберите все сочетания таблиц при условии, что дата календаря попадает между началом и концом.
...
Рейтинг: 0 / 0
11.02.2022, 19:28
    #40133615
alexandrDr87
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
Спасибо всем кто обратил внимание на тему.
У меня все же получилось это сделать. Надеюсь на конструктивную критику и возможно подсказки по оптимизации. На данный момент как по мне работает достаточно сносно. 10 000 строк обрабатывает 18 секунд.

Код: 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.
CREATE PROCEDURE [dbo].[spLoad]
AS
BEGIN
  SET NOCOUNT ON;

  TRUNCATE TABLE tAppeals;

  DECLARE @appeal_id nvarchar(100);
  DECLARE @open_time datetime;
  DECLARE @close_time datetime;

  DECLARE appeals_cursor CURSOR FOR
    SELECT INCIDENT_ID, OPEN_TIME, CLOSE_TIME
    FROM OPENQUERY([server], 'SELECT [INCIDENT_ID], [OPEN_TIME], [CLOSE_TIME]
                                                   FROM [base].[dbo].[table]
                                 ')
 
  OPEN appeals_cursor

  FETCH NEXT FROM appeals_cursor INTO @appeal_id, @open_time, @close_time

  WHILE @@FETCH_STATUS = 0
  BEGIN
       DECLARE @new_open_time datetime;
       DECLARE @new_close_time datetime;
       DECLARE @final_close_time datetime;

       SET @final_close_time = @close_time;
       SET @new_open_time = @open_time;

       WHILE DATEPART(month, @new_open_time) < DATEPART(MONTH, @close_time)
       BEGIN
           SET @new_close_time = CONCAT(CONVERT(nvarchar(10), CONVERT(date, EOMONTH(@open_time))), ' ' +  '23:59:59.000')

           INSERT INTO tAppeals (appeal_id, open_time, close_time) VALUES(@appeal_id, @open_time, @new_close_time)

           SET @new_open_time = DATEADD(MONTH, 1 + DATEDIFF(MONTH, 0, @open_time), 0);
            SET @open_time = @new_open_time;
       END;

       IF DATEPART(month, @new_open_time) = DATEPART(MONTH, @close_time)
           INSERT INTO tAppeals (appeal_id, open_time, close_time) VALUES(@appeal_id, @open_time, @final_close_time)

       FETCH NEXT FROM appeals_cursor INTO @appeal_id, @open_time, @close_time
  END
CLOSE appeals_cursor
END;
GO
...
Рейтинг: 0 / 0
11.02.2022, 20:25
    #40133625
alexeyvg
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
alexandrDr87
У меня все же получилось это сделать
На что только люди не идут, что бы не делать таблицу-календарь :-(
...
Рейтинг: 0 / 0
11.02.2022, 21:22
    #40133634
alexandrDr87
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
alexeyvg
alexandrDr87
У меня все же получилось это сделать
На что только люди не идут, что бы не делать таблицу-календарь :-(


Покажите решение с календарем, я не понимаю что вы имеете ввиду
...
Рейтинг: 0 / 0
11.02.2022, 21:23
    #40133635
godsql
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
declaRE @T TABLE
(
INCIDENT VARCHAR(20),
OPEN_TIME DATETIME ,
CLOSE_TIME  DATETIME 
)

INSERT INTO @T  (INCIDENT , OPEN_TIME , CLOSE_TIME)
VALUES ('IN123456', '2022-01-03T08:41:22.000','2022-05-10T09:40:27.000')
INSERT INTO @T  (INCIDENT , OPEN_TIME , CLOSE_TIME)
VALUES ('IN123987', '2022-04-15T08:41:22.000','2022-08-01T09:40:27.000')


SELECT T.*, T2.NUM, 
	IIF(T2.NUM=1, T.OPEN_TIME, (DATEADD(day,1,EOMONTH(dateadd(MM, T2.NUM-2,T.OPEN_TIME))))) AS OPEN_TIME1,
	IIF(T2.NUM=DATEDIFF(MM,T.OPEN_TIME, T.CLOSE_TIME) +1, T.CLOSE_TIME
			, EOMONTH(dateadd(MM, T2.NUM-1,T.OPEN_TIME))) AS CLOSE_TIME1
	 FROM @T T
CROSS APPLY ( 
	SELECT TOP (DATEDIFF(MM,T.OPEN_TIME, T.CLOSE_TIME) +1) 
	ROW_NUMBER() OVER (ORDER BY 1/0) AS NUM
	FROM SYS.all_objects) T2
...
Рейтинг: 0 / 0
12.02.2022, 05:46
    #40133672
PizzaPizza
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
Когда вам кажется что в РБД вам надо сделать курсор, вам надо либо пересмотреть хранение ваших данных либо почитать функционал вашей базы данных.

alexandrDr8710 000 строк обрабатывает 18 секунд.
И это только начало. Курсор вам и не такое выделает
...
Рейтинг: 0 / 0
12.02.2022, 09:09
    #40133677
LexusR
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
Код: 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.
declaRE @T TABLE
(
INCIDENT VARCHAR(20),
OPEN_TIME DATETIME ,
CLOSE_TIME  DATETIME 
)

INSERT INTO @T  (INCIDENT , OPEN_TIME , CLOSE_TIME)
VALUES ('IN123456', '2022-01-03T08:41:22.000','2022-05-10T09:40:27.000')
INSERT INTO @T  (INCIDENT , OPEN_TIME , CLOSE_TIME)
VALUES ('IN123987', '2022-04-15T08:41:22.000','2022-08-01T09:40:27.000')



;WITH Result AS (SELECT INCIDENT , OPEN_TIME , CLOSE_TIME, 0 as [Shift]
,OPEN_TIME as OPEN_TIME1
,IIF(EOMONTH(OPEN_TIME,0)< CLOSE_TIME,EOMONTH(OPEN_TIME,0),CLOSE_TIME) AS CLOSE_TIME1
FROM @T
UNION ALL
SELECT r.INCIDENT , r.OPEN_TIME , r.CLOSE_TIME,r.[Shift]+1 as [Shift]
,DATEADD(dd,1,r.CLOSE_TIME1) as OPEN_TIME1
,IIF(EOMONTH(r.OPEN_TIME,r.[Shift]+1)< r.CLOSE_TIME,EOMONTH(r.OPEN_TIME,r.[Shift]+1),r.CLOSE_TIME) AS CLOSE_TIME1
FROM @T t
JOIN Result r on r.INCIDENT = t.INCIDENT and r.CLOSE_TIME1 < r.CLOSE_TIME
)
SELECT INCIDENT,OPEN_TIME,CLOSE_TIME,OPEN_TIME1,CLOSE_TIME1
FROM Result ORDER BY INCIDENT , OPEN_TIME1

alexandrDr87,
...
Рейтинг: 0 / 0
12.02.2022, 13:45
    #40133732
alexeyvg
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
alexandrDr87
alexeyvg
пропущено...
На что только люди не идут, что бы не делать таблицу-календарь :-(


Покажите решение с календарем, я не понимаю что вы имеете ввиду
Вам выше написали:
Владислав Колосов
составьте календарь последних дней месяца, выберите все сочетания таблиц при условии, что дата календаря попадает между началом и концом.
Тут же очевидно всё.
Есть календарь, делаете к нему джойн вашей таблицы
В результате для одной записи из вашей таблицы получается одна запись на каждую запись в календаре, с которой она джойнится по условию соединения.

Помощь с конкретным кодом предполагает, что вы предоставили этот код, с созданием таблиц и наполнением их данными, тут люди сделали за вас черновую работу...

Пример кода от godsql - это тоже по сути решения с календарём, только он формируется на лету, прямо в запросе.
А код от LexusR - это как бы курсор, только в виде CTE

А вот пример с календарём в чистом виде:
Код: 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.
declare @Calendar TABLE
(
	num int,
	start_time datetime,
	next_time datetime,
	close_time datetime
)
insert @Calendar
select num, dateadd(mm, num - 1, '20200101'), dateadd(mm, num, '20200101'), dateadd(mi, -1, dateadd(mm, num, '20200101'))
from (SELECT ROW_NUMBER() OVER (ORDER BY 1/0) AS NUM FROM SYS.all_objects) n

declaRE @T TABLE
(
	INCIDENT VARCHAR(20),
	OPEN_TIME DATETIME ,
	CLOSE_TIME  DATETIME 
)

INSERT INTO @T  (INCIDENT , OPEN_TIME , CLOSE_TIME)
VALUES ('IN123456', '2022-01-03T08:41:22.000','2022-05-10T09:40:27.000')
INSERT INTO @T  (INCIDENT , OPEN_TIME , CLOSE_TIME)
VALUES ('IN123987', '2022-04-15T08:41:22.000','2022-08-01T09:40:27.000')


select t.INCIDENT, case when t.OPEN_TIME > c.start_time then t.OPEN_TIME else c.start_time end as OPEN_TIME, case when t.CLOSE_TIME < c.next_time then t.CLOSE_TIME else c.close_time end as CLOSE_TIME
from @T t
	join @Calendar as c on c.start_time < t.CLOSE_TIME and c.next_time > t.OPEN_TIME
order by INCIDENT, OPEN_TIME
...
Рейтинг: 0 / 0
15.02.2022, 02:19
    #40134121
ValK412
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как сделать отбор
alexandrDr87,
если попробуете такой вариант с рекурсией, будет интересно сравнить время выполнения с текущей Вашей реализацией
Код: 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.
declaRE @T TABLE(INCIDENT VARCHAR(20),OPEN_TIME DATETIME ,CLOSE_TIME  DATETIME )

INSERT INTO @T  (INCIDENT , OPEN_TIME , CLOSE_TIME) VALUES 
('IN123450', '2020-01-01T08:41:22.000','2020-03-01T09:40:27.000') -- високосный
,('IN123456', '2022-01-03T08:41:22.000','2022-05-10T09:40:27.000')
,('IN123987', '2022-04-15T08:41:22.000','2022-08-01T09:40:27.000')  --начало месяца в конце
,('IN123501', '2022-01-03T08:41:22.000','2022-01-10T09:40:27.000');

with tm as (
select incident,1 as num, open_time as ot , close_time ct 
	,dateadd(d,-datepart(d,open_time)+1,open_time) as bMon
	from @t as t
union all
	select tm.incident, num+1, tm.ot,tm.ct
	,dateadd(m,1,tm.bMon)
	from tm inner join @t as t on  t.INCIDENT=tm.incident
	where dateadd(m,1,tm.bMon)<=ct
)

select INCIDENT,num,
	case when (ot>bMon) then ot else cast(bMon as date) end as open_timeN,
	case when (datediff(m,bMon,ct)=0) then  ct 
	  else -- на 2 миллисекунды меньше начала следующего дня (месяца)
		dateadd(ms,-2,cast(dateadd(m,1,cast(bMon as date)) as datetime)) 
	end as close_timeN
	from tm
order by INCIDENT,num
...
Рейтинг: 0 / 0
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Подскажите как сделать отбор / 10 сообщений из 10, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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