2016-10-05 3 views
2

У меня есть рабочие и месяцы, в которых они работали. Я хочу выяснить, кто из рабочих имел время. Рассмотрим простой пример:Проверьте, являются ли диапазоны дат последовательными или содержат пробелы в каждой категории

enter image description here

На графике это выглядит следующим образом: enter image description here

Желаемый результат:

+-----------+-----+ 
| worker | gap | 
+-----------+-----+ 
| worker001 | 1 | 
+-----------+-----+ 
| worker002 | 1 | 
+-----------+-----+ 
| worker003 | 0 | 
+-----------+-----+ 

Предположения:

  • У каждого работника может быть другая дата начала и другая дата окончания .
  • Таблица содержит только даты (месяцы), когда работник находился в работе.

Я бы приветствовал запрос, но я был бы благодарен за идею, как это сделать. Моя идея такова.

Проверьте минимальные и максимальные сроки для каждого работника. Создавайте последовательности месяцев между этими двумя днями для каждого работника (не знаете, как). Присоедините его к исходной таблице и проверьте, нет ли нулей. Если это так, у нас есть пробелы.

+0

Как вы получаете значение «1» для «worker001»? Кроме того, пометьте свой вопрос в базе данных, которую вы используете. –

+0

Какая СУБД вы используете? –

+0

Алгоритм должен работать при изменении года? – Nemeros

ответ

3

Довольно простой способ подсчета пробелов заключается в подсчете количества месяцев и посмотреть на разницу между максимумом и минимумом:

select worker, 
     (datediff(month, min(month), max(month)) + 1) - count(*) as nummissing 
from t 
group by worker; 

Примечание: При этом используется синтаксис SQL Server для разницы в месяцы; это просто удобство, большинство других баз данных имеют аналогичную функциональность.

+0

Удивительно просто :-) –

+0

Тогда вам нужно подсчитать количество отсутствующих рабочих месяцев, а не количество пробелов? – Nemeros

+0

@Nemeros В вопросе я попросил список рабочих с пробелами. Каждый работник недоволен или нет. Но да, вы правы! этот ответ учитывает недостающие месяцы, а не количество пробелов. Количество пробелов было бы велико, хотя я не просил об этом. –

1

Я бы пошел с некоторым алгоритмом функции окна. тестовые данные

входа:

create table tt (key varchar(10), dte date); 

insert into tt values ('w1', '2017-12-01'); 
insert into tt values ('w1', '2017-11-01'); 
insert into tt values ('w1', '2015-12-01'); 
insert into tt values ('w1', '2015-11-01'); 
insert into tt values ('w1', '2016-01-01'); 
insert into tt values ('w1', '2016-02-01'); 
insert into tt values ('w1', '2016-05-01'); 
insert into tt values ('w1', '2016-06-01'); 
insert into tt values ('w2', '2016-02-01'); 
insert into tt values ('w2', '2016-03-01'); 
insert into tt values ('w2', '2016-05-01'); 
insert into tt values ('w3', '2016-01-01'); 
insert into tt values ('w3', '2016-02-01'); 

Затем мы используем функцию окна, чтобы создать группу между датой для непрерывного извлечь рабочее время.

Отсюда нам просто нужно сделать счет - 1, с другой группой по выражению, чтобы иметь количество пробелов.

with tmp as (
select key, 
dte, 
dte - (row_number() over(partition by key order by dte) MONTHS) as rnk_month, 
row_number() over(partition by key order by dte) as rnk_tot 
from tt) 
select key, min(dte), max(dte), rnk_month 
from tmp 
group by key, rnk_month 
order by key, rnk_month 

Пробуя запрос с помощью синтаксиса sqlServer ...(Не уверен, что он будет работать, пока я не SQLs)

with tmp as (
select key, 
dte, 
dateadd(month, - row_number() over(partition by key order by dte), dte) as rnk_month, 
row_number() over(partition by key order by dte) as rnk_tot 
from tt) 
select key, min(dte), max(dte), rnk_month 
from tmp 
group by key, rnk_month 
order by key, rnk_month 

Чтобы объяснить немного больше:

Функция: row_number() над (раздел по ключевому распоряжению DTE) будет outpout это для работника 1:

----------------------------- 
Worker | Month  | rnk_tot 
----------------------------- 
w1  |2015-11-01 | 1 
w1  |2015-12-01 | 2 
w1  |2016-01-01 | 3 
w1  |2016-02-01 | 4 
w1  |2016-05-01 | 5 
w1  |2016-06-01 | 6 
w1  |2017-11-01 | 7 
w1  |2017-12-01 | 8 

Теперь, если мы вычтем дату с учетом rnk_tot rnk_tot как месяц, мы будем иметь некоторые новые даты, которые образуют некоторую непрерывную группу:

---------------------------------------- 
Worker | Month  | rnk_tot | rnk_month 
---------------------------------------- 
w1  |2015-11-01 | 1  |2015-10-01 
w1  |2015-12-01 | 2  |2015-10-01 
w1  |2016-01-01 | 3  |2015-10-01 
w1  |2016-02-01 | 4  |2015-10-01 
w1  |2016-05-01 | 5  |2015-12-01 
w1  |2016-06-01 | 6  |2015-12-01 
w1  |2017-11-01 | 7  |2017-04-01 
w1  |2017-12-01 | 8  |2017-04-01 

Отсюда вы можете сделать группу, на работника и rnk_month столбца Gatter непрерывного рабочего времени (это то, что запрос я отправил сделать):

---------------------------------------- 
Worker | Mth Min | Mth Max | rnk_month 
---------------------------------------- 
w1  |2015-11-01 |2016-02-01|2015-10-01 
w1  |2016-05-01 |2016-06-01|2015-12-01 
w1  |2017-11-01 |2017-12-01|2017-04-01 

И здесь вы можете иметь номер пробелов. Таким образом, окончательный запрос может быть:

with tmp as (
select key, 
dateadd(month, - row_number() over(partition by key order by dte), dte) as rnk_month 
from tt) 
select key, count(distinct rnk_month) - 1 as gaps 
from tmp 
group by key 

Это даст этот выход для теста я использовал:

----------------- 
Worker | Gaps 
----------------- 
w1  | 2 
w2  | 1 
w3  | 0 
+0

В этом коде есть некоторая ошибка. Рядом с словом МЕСЯЦ. По крайней мере, SQL Server. Что это значит? Что такое rnk_month? –

+0

Да, я использовал db2, прежде чем вы указали, что это для SqlS. В основном rnk_month будет Date. больше в ответ на более чистое объяснение (я собираюсь отредактировать его в нескольких) – Nemeros

+0

проверить второй запрос, который должен работать с sqls, я объясню основной механизм запроса – Nemeros