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

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

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

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

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

единственное, что нужно будет ввести "мнимую" начальную точку, которая будет предшествовать всем рассматриваемым интервалам отсутствия человека на работе... Чтобы первый интервал работоспособности собрать правильно...
...
Рейтинг: 0 / 0
23.01.2018, 14:13
    #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
23.01.2018, 14:19
    #39589133
undll
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавить недостающие периоды
Kopelly, огромное спасибо!
...
Рейтинг: 0 / 0
23.01.2018, 14:24
    #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
23.01.2018, 14:26
    #39589143
Добавить недостающие периоды
Kopelly,

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

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

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

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

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


Попробуй управляющие конструкции:
==> (для подсветки строки жёлтым)
>>> <<< (для подсветки нескольких символов в строке)
...
Рейтинг: 0 / 0
23.01.2018, 14:58
    #39589185
Добавить недостающие периоды
Kopelly (без сарказма). теперь уже завтра . Сегодня рабочий день закончился....
...
Рейтинг: 0 / 0
23.01.2018, 15:47
    #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
23.01.2018, 15:58
    #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
23.01.2018, 16:18
    #39589304
Добавить недостающие периоды
Wlr-l,

Под «разверни» таки подразумевалось именно что - «разверни», а не «сгенерируй все даты в заданном диапазоне». То есть, любой отрезок из исходного набора данных даст две точки : «начало» и «кончало». Вот и нужно пересобрать в новые интервалы...
...
Рейтинг: 0 / 0
23.01.2018, 16:20
    #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
23.01.2018, 16:24
    #39589312
Wlr-l
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавить недостающие периоды
Добрый Э - Эх,

Согласен. Неточность в терминологии. Будем считать, что я просто привел вариант решения.
...
Рейтинг: 0 / 0
23.01.2018, 16:39
    #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
23.01.2018, 17:31
    #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
23.01.2018, 17:39
    #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
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Добавить недостающие периоды / 25 сообщений из 30, страница 1 из 2
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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