Расчет общего времени, которое выходит за пределы набора данных в диапазоне дат / времени - PullRequest
3 голосов
/ 06 января 2010

Я хочу добавить новые функции в существующее приложение (база данных - Microsoft SQL 2005). По сути, я пытаюсь подсчитать, сколько минут (или секунд) определенного отдела было «беспилотным» для определенного набора диапазонов дат. Я ищу запросить набор данных в идеале с одним утверждением. У меня есть подпрограмма, которая просматривает набор записей, анализирует его и выдает ответ, но это довольно уродливо. У кого-нибудь есть какие-либо предложения о том, как я могу оптимизировать его для удобства чтения, используя чистый SQL - или даже какие-нибудь указатели / статьи о том, на что я должен обратить внимание, мой Googlefu подводит меня.

Я думаю, в некотором смысле это почти как поиск в календаре "свободного времени", но сгруппированный.

Вот примерный набор данных, чтобы дать вам представление о том, с чем я работаю (по сути, коллеги активируют, а затем выключают). Для простоты я использую округление до минут ниже, но, скорее всего, буду рассчитывать в секундах.

------------------------------------------------------------------------
| Colleague Id | Department Id   | Date In          | Date Out         |
------------------------------------------------------------------------
| 1            | 1               | 04/01/2010 08:45 | 04/01/2010 11:45 |
| 2            | 1               | 04/01/2010 09:00 | 04/01/2010 12:15 |
| 3            | 1               | 04/01/2010 10:00 | 04/01/2010 12:00 |
| 4            | 1               | 04/01/2010 12:30 | 04/01/2010 17:00 |
| 1            | 1               | 04/01/2010 12:45 | 04/01/2010 17:15 |
| 3            | 1               | 04/01/2010 13:00 | 04/01/2010 17:25 |
| 5            | 2               | ...              | ...              |
------------------------------------------------------------------------

Так, например, если я запросил приведенную выше таблицу для Идентификатора отдела = 1, между 04/01/2010 08: 30: 00 и 04/01/2010 17:30:00 , я ожидал бы результат 35 минут (или 2100 секунд) «беспилотного времени» (это сумма времени в начале, в середине и в конце диапазона, который беспилотный).

Ответы [ 3 ]

3 голосов
/ 06 января 2010

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

Учитывая, что вы хотите:

drop table foo 
go

create table foo (
   c_id int not null,
   d_id int not null,
   datein datetime not null,
   dateout datetime not null
)
go


insert into foo values (1, 1, '04/01/2010 08:45', '04/01/2010 11:45')
insert into foo values (2, 1, '04/01/2010 09:00', '04/01/2010 12:15')
insert into foo values (3, 1, '04/01/2010 10:00', '04/01/2010 12:00')
insert into foo values (4, 1, '04/01/2010 12:30', '04/01/2010 17:00')
insert into foo values (1, 1, '04/01/2010 12:45', '04/01/2010 17:15')
insert into foo values (3, 1, '04/01/2010 13:00', '04/01/2010 17:25')
go


drop procedure unmanned
go

create procedure unmanned
   @d_id int,
   @start datetime,
   @end datetime

as

select distinct dateadd(ss,i_int,@start)
 from Integers 
      left join foo on dateadd(ss,i_int,@start) >= datein and dateadd(ss,i_int,@start) < dateout


where i_int between 0 and 60*60*24
and dateadd(ss,i_int,@start) >= @start and dateadd(ss,i_int,@start)< @end
and datein is null
order by 1

go

exec unmanned 1, '4/1/10 8:30', '4/1/10 17:30'
1 голос
/ 06 января 2010

Это проблема пересечения диапазонов: вы смотрите на диапазон чисел:

4/01/2010 08:30:00 - 04/01/2010 17:30:00  

этот диапазон может быть представлен в виде чисел - микросекунд или секунд с начала дня, например:

[1000000, 3000000]

и вы хотите найти его части, которые не сталкиваются ни с одним из:

[1200000, 1250000]
[1250000, 1490000]
[1500000, 1950000]
...

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

Edit:

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

0 голосов
/ 06 января 2010

Я бы рекомендовал использовать подход Эрика Х. С этим отказом от ответственности это немного противно, но оно предлагает способ сделать то же самое, если у вас нет доступа к таблице чисел по той или иной причине. Я уверен, что это можно улучшить, я просто хотел попробовать это без таблицы чисел:

Declare @Start DateTime, @End DateTime

Select @Start = '04/01/2010 09:30'
    , @End = '04/01/2010 17:30'

--Table Creation Stuff
Declare @y Table (ColleagueId Int, DepartmentId Int, DateIn DateTime, DateOut DateTime)

Insert @y
Select 1, 1, '04/01/2010 08:45' , '04/01/2010 11:45'
Union All Select 2 , 1, '04/01/2010 09:00' , '04/01/2010 12:15'
Union All Select 3 , 1, '04/01/2010 10:00' , '04/01/2010 12:00'
Union All Select 4 , 1, '04/01/2010 12:30' , '04/01/2010 17:00' 
Union All Select 1 , 1, '04/01/2010 12:45' , '04/01/2010 17:15' 
Union All Select 3 , 1, '04/01/2010 13:00' , '04/01/2010 17:25'
---------

Select DateDiff(minute, @Start, @End)  -- TotalTime
     - Sum(DateDiff(minute, 
        Case When DateIn < @Start Then @Start Else DateIn End, 
        Case When DateOut > @End Then @End Else DateOut End)) --StaffedTime
     as UnmannedTime
From
(
    Select Min(din) DateIn, dout DateOut
    From
    (
        Select Min(y.DateIn) din, Max(y2.DateOut) dout
        From @y y
        Inner Join @y y2 on y.DateOut >= y2.DateIn
        --you probably want to close the other end of these filters, but leave some room
        --(to handle the guy who started @ 7:45, etc...)
        Where y.DateIn < @End 
            and y2.DateOut > @Start             
        Group By y.DateIn
    ) x 
    Group By dout
) q

edit добавлены вышеприведенные операторы case для обработки вычисления StaffedTime, когда определенный период начинается до @Start (или заканчивается после @End)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...