powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Добавить недостающие периоды
25 сообщений из 30, страница 1 из 2
Добавить недостающие периоды
    #39589040
undll
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть таблица, с данными об отпусках и больничных (Таблица №1)
Нужно получить итоговую таблицу за год (как пример 2017) с вставкой периодов работы, т.е. дополнить промежуточные периоды (Таблица №2). Т.е. добавится период до первого отпуска (если отпуск не начался 01.01.2017) и между периодами отсутствия.

...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589041
Фотография Akina
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Версия сервера? LEAD/LAG понимает?
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589042
undll,

разверни интервалы в точки. затем из точек снова собери интервалы, но уже правильные, с недостающими периодами работоспособности...
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589068
undll
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
AkinaВерсия сервера? LEAD/LAG понимает?
2008 R2, как я понимаю там нету LEAD'а
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589073
undll,

зато есть outer apply и CTE (но CTE тут нужно исключительно для декомпозиции и упрощения исходного запроса) ;)
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589074
Фотография Akina
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну тогда самое простое решение - озвучено. Только при разборке обязательно пометить точки - где начало, а где конец отрезка. Перед сборкой - добавить точки начала и конца целевого периода, а после сборки - удалить интервалы нулевой длины.
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589082
Akina,

на самом деле всё еще проще - достаточно получить УНИКАЛЬНЫЙ набор точек, без дублей. Далее - собрать точки в новые интервалы. Для определения типа интервала (пил/курил/болел) - пересечь левым джойном с исходными диапазонами... Что не найдет себе пересечения - то периоды работоспособности
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589088
Добрый Э - Эх,

единственное, что нужно будет ввести "мнимую" начальную точку, которая будет предшествовать всем рассматриваемым интервалам отсутствия человека на работе... Чтобы первый интервал работоспособности собрать правильно...
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589119
Kopelly
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: 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.
set dateformat ymd
declare @t table (date_from smalldatetime,
                  date_to smalldatetime,
				  type varchar(100));
insert into @t values
('2017-03-05','2017-04-20','Отпуск'),
('2017-04-20','2017-04-23','Больничный'),
('2017-06-01','2017-06-14','Отпуск'),
('2017-09-01','2017-09-01','Отпуск');

declare @period table (date_from smalldatetime,
                       date_to smalldatetime);
insert into @period values
('2017-01-01','2017-12-31');


with begins as (
Select a.date_to+1 as date_from
  From (Select date_to From @t 
         union 
		Select date_from-1 From @period
          ) a
Where not exists (Select 1 From @t b Where b.date_to>a.date_to and a.date_to between b.date_from and b.date_to)),
ends as (
Select a.date_from-1  as date_to
  From (Select date_from From @t 
         union 
		Select date_to+1 From @period
          ) a
Where not exists (Select 1 From @t b Where b.date_from<a.date_from and a.date_from between b.date_from and b.date_to))
Select * From @t union all
Select *,(Select min(ends.date_to) From ends WHere ends.date_to>begins.date_from) as Date_To,'Работал' as Type From begins 
Order by 1
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589133
undll
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Kopelly, огромное спасибо!
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589140
Kopelly
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
undll,

так правильнее (в предыдущем вылазил интервал работы c 02.09.2018 по NULL)
Код: 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.
set dateformat ymd
declare @t table (date_from smalldatetime,
                  date_to smalldatetime,
				  type varchar(100));
insert into @t values
('2016-03-05','2017-04-20','Отпуск'),
('2017-04-20','2017-04-23','Больничный'),
('2017-06-01','2017-06-14','Отпуск'),
('2017-09-01','2018-09-01','Отпуск');

declare @Begin smalldatetime = '2017-01-01',
        @End   smalldatetime = '2017-12-31';

with begins as (
Select a.date_to+1 as date_from
  From (Select date_to From @t 
         union 
		Select @Begin-1
          ) a
Where not exists (Select 1 From @t b 
                   Where b.date_to>a.date_to 
				     and a.date_to between b.date_from and b.date_to)
  and a.date_to-1 <= @End),
ends as (
Select a.date_from-1  as date_to
  From (Select date_from From @t 
         union 
		Select @End+1
          ) a
Where not exists (Select 1 From @t b 
                   Where b.date_from<a.date_from 
				     and a.date_from between b.date_from and b.date_to))
Select * From @t union all
Select *,(Select min(ends.date_to) From ends WHere ends.date_to>begins.date_from) as Date_To,'Работал' as Type From begins 
Order by 1
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589143
Kopelly,

Много лишнего. Можно проще.
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589145
undll
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Kopelly,

там в 1 отпуске опечатка, период 05.03.2017 - 19.03 .2017
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589151
Kopelly
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый Э - ЭхKopelly,

Много лишнего. Можно проще.
С удовольствием посмотрел бы на более красивое и оптимальное решение (без сарказма).
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589155
Kopelly
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
undllKopelly,

там в 1 отпуске опечатка, период 05.03.2017 - 19.03 .2017
Я специально изменил периоды, чтобы были пересекающиеся и выходящие за рамки года. Для твоих данных смотри заполнение таблицы @t из первого скрипта.
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589182
Kopellyтак правильнее

Код: sql
1.
2.
3.
Надо как-то учиться выделять код в тэге src, 
чтобы показывать изменения. 
А то ведь оно непросто искать десять отличий ;)


Попробуй управляющие конструкции:
==> (для подсветки строки жёлтым)
>>> <<< (для подсветки нескольких символов в строке)
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589185
Kopelly (без сарказма). теперь уже завтра . Сегодня рабочий день закончился....
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589264
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Как уже сказали: "разверни интервалы в точки. затем из точек снова собери интервалы, но уже правильные, с недостающими периодами работоспособности... "

Код: 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.
declare @p1 date = '20170101', @p2 date = '20171231';

with A as (--Таблица дат
	 select number as i, cast(dateadd(dd,number,@p1) as date) as dt from master.dbo.spt_values where type='P'
)
--select * from A;
,    B as (--Таблица № 1
     select *
       from (values ('20170305','20170319','Отпуск'),
                    ('20170420','20170423','Больничный'),
                    ('20170601','20170614','Отпуск'),
                    ('20170901','20170901','Отпуск')
            ) as T(          d1,        d2,   tp)
)
,    C as (--Разворачиваем интервалы в "точки"
     select i,dt, isnull(tp,'Работал') as tp 
	   from      A b
	   left join B c on dt between d1 and d2
	   where dt between @p1 and @p2
)
--select * from C;
,    D as (--Находим интервалы непрерывности tp
     select i,dt,tp,i-rank() over (partition by tp order by tp,i) as rnk
	   from      C b
)
--select * from D
select min(dt) as date_from, max(dt) as date_to, tp --Формируем периоды
  from D
  group by rnk,tp
  order by date_from;



Результат:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
date_from	date_to	tp
2017-01-01	2017-03-04	Работал
2017-03-05	2017-03-19	Отпуск
2017-03-20	2017-04-19	Работал
2017-04-20	2017-04-23	Больничный
2017-04-24	2017-05-31	Работал
2017-06-01	2017-06-14	Отпуск
2017-06-15	2017-08-31	Работал
2017-09-01	2017-09-01	Отпуск
2017-09-02	2017-12-31	Работал
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589282
Руслан Дамирович
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LEAD и LAG для слабаков!
Код: 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.
DECLARE @off TABLE ( [date_from] DATE, [date_to] DATE, [type] VARCHAR(50) )
INSERT INTO
  @off
VALUES 
  ( '20170305', '20170319', 'Сказочный Бали!' ),
  ( '20170420', '20170423', 'Что ж я маленьким не сдох?!' ),
  ( '20170601', '20170614', 'Тагиииил!' ),
  ( '20170901', '20170901', 'Дважды два - четыре!' )
;
WITH
t0 AS (
  SELECT
    [rn] = ROW_NUMBER() OVER ( ORDER BY [date_from] ),
    *
  FROM
    @off
),
t1 AS (
SELECT
  [date_from],
  [date_to],
  [type]
FROM
  t0
UNION ALL
SELECT
  [date_from] = CONVERT( DATE, '20170101' ),
  [date_to] = DATEADD( DAY, -1, tc.[date_from] ),
  [type] = 'Йа маленькая лааашадка'
FROM
  t0 tc
WHERE
  tc.[rn] = 1
UNION ALL
SELECT
  [date_from] = DATEADD( DAY, 1, tc.[date_to] ),
  [date_to] = DATEADD( DAY, -1, ISNULL( tn.[date_from], '20180101' ) ),
  [type] = 'Йа маленькая лааашадка'
FROM
  t0 tc
  LEFT JOIN t0 tn ON (
        tn.[rn] = tc.[rn] + 1 )
)
SELECT
  *
FROM
  t1
ORDER BY
  [date_from]
;
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589304
Wlr-l,

Под «разверни» таки подразумевалось именно что - «разверни», а не «сгенерируй все даты в заданном диапазоне». То есть, любой отрезок из исходного набора данных даст две точки : «начало» и «кончало». Вот и нужно пересобрать в новые интервалы...
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589309
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Руслан Дамирович,

Наверно, ДА, для слабаков.

Например, если взять такую строку исходных данных
Код: sql
1.
  ( '20170101', '20170319', 'Сказочный Бали!' ),


Получим
Код: plaintext
1.
2.
3.
4.
date_from	date_to	type
2017-01-01	2017-03-19	Сказочный Бали!
2017-01-01	2016-12-31	Йа маленькая лааашадка
2017-03-20	2017-04-19	Йа маленькая лааашадка
2017-04-20	2017-04-23	Что ж я маленьким не сдох?!

Т.е. имеем проблему на границе периода.
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589312
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый Э - Эх,

Согласен. Неточность в терминологии. Будем считать, что я просто привел вариант решения.
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589325
Wlr-lРуслан Дамирович,

Наверно, ДА, для слабаков.

Например, если взять такую строку исходных данных
Код: sql
1.
  ( '20170101', '20170319', 'СказочноЕ бали! :)' ),


Получим
Код: plaintext
1.
2.
3.
4.
date_from	date_to	type
2017-01-01	2017-03-19	Сказочный Бали!
2017-01-01	2016-12-31	Йа маленькая лааашадка
2017-03-20	2017-04-19	Йа маленькая лааашадка
2017-04-20	2017-04-23	Что ж я маленьким не сдох?!

Т.е. имеем проблему на границе периода.
"если вы понимаете, о чем я..."(с)
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589386
Wlr-l
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый Э - Эх, у меня тоже заканчивается рабочий день. Приведу еще один пример, разворота интервалов в точки:

Код: 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.
declare @p1 date = '20170101', @p2 date = '20171231';

with B as (--Таблица № 1
     select *
       from (values ('20170305','20170319','Отпуск'),
                    ('20170420','20170423','Больничный'),
                    ('20170601','20170614','Отпуск'),
                    ('20170901','20170901','Отпуск')
            ) as T(          d1,        d2,   tp)
)
,    C as (--Разворачиваем интервалы в "точки"
	 select d1 as dt, 0 as b from B -- 0 = «начало»  
	 union
	 select d2,1      from B        -- 1 = «кончало»
	 union
	 select @p1,0
	 union
	 select @p2,1
)
--select * from C;
,    D as (--просто пронумеруем "точки"
	 select cast(dt as date) as dt, b, row_number() over (order by dt) rn
	   from C
)
--select * from D;
--Сформируем новые интервалы
select case when dl.b=0  then dl.dt else dateadd(dd, 1, dl.dt) end as date_from,
       case when dr.b<>0 then dr.dt else dateadd(dd,-1, dr.dt) end as date_to
  from D dl
  join D dr on dl.rn=dr.rn-1;


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Результат:
2017-01-01	2017-03-04
2017-03-05	2017-03-19
2017-03-20	2017-04-19
2017-04-20	2017-04-23
2017-04-24	2017-05-31
2017-06-01	2017-06-14
2017-06-15	2017-08-31
2017-09-01	2017-09-01
2017-09-02	2017-12-31

Описание периода ТС добавит.
...
Рейтинг: 0 / 0
Добавить недостающие периоды
    #39589394
Руслан Дамирович
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Wlr-lРуслан Дамирович,
Например, если взять такую строку исходных данных
Код: sql
1.
  ( '20170101', '20170319', 'Сказочный Бали!' ),


Получим
Код: plaintext
1.
2.
3.
4.
date_from	date_to	type
2017-01-01	2017-03-19	Сказочный Бали!
2017-01-01	2016-12-31	Йа маленькая лааашадка
2017-03-20	2017-04-19	Йа маленькая лааашадка
2017-04-20	2017-04-23	Что ж я маленьким не сдох?!
Т.е. имеем проблему на границе периода.
Проблемы нет. Достаточно добавить пару условий.
Ваше решение универсальное, мое - в лоб.
Я бы тоже решил его в виде HCTE, но зачем? :)

авторСказочноЕ бали! :)
И все же, остров, а значит м.р. - сказочный. Но мы все понимаем, что же там происходило ;)
...
Рейтинг: 0 / 0
25 сообщений из 30, страница 1 из 2
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Добавить недостающие периоды
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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