Поиск одновременных событий в базе данных между
У меня есть база данных, в которой хранятся записи телефонных звонков. Каждая запись телефонного звонка имеет время начала и время окончания. Я хочу узнать, каков максимальный объем телефонных звонков, которые происходят одновременно, чтобы узнать, превысили ли мы количество доступных телефонных линий в нашем телефонном банке. Как я могу решить эту проблему?
Ответы
Ответ 1
Учитывая тот факт, что максимальное количество соединений будет точкой StartTime, вы можете
SELECT TOP 1 count(*) as CountSimultaneous
FROM PhoneCalls T1, PhoneCalls T2
WHERE
T1.StartTime between T2.StartTime and T2.EndTime
GROUP BY
T1.CallID
ORDER BY CountSimultaneous DESC
Запрос возвращает для каждого вызова количество одновременных вызовов. Либо закажите их по убыванию, либо выберите первый или SELECT MAX (CountSimultaneous) из вышеперечисленного (в качестве подзапроса без заказа и без TOP).
Ответ 2
попробуйте следующее:
DECLARE @Calls table (callid int identity(1,1), starttime datetime, endtime datetime)
INSERT @Calls (starttime,endtime) values ('6/12/2010 10:10am','6/12/2010 10:15am')
INSERT @Calls (starttime,endtime) values ('6/12/2010 11:10am','6/12/2010 10:25am')
INSERT @Calls (starttime,endtime) values ('6/12/2010 12:10am','6/12/2010 01:15pm')
INSERT @Calls (starttime,endtime) values ('6/12/2010 11:10am','6/12/2010 10:35am')
INSERT @Calls (starttime,endtime) values ('6/12/2010 12:10am','6/12/2010 12:15am')
INSERT @Calls (starttime,endtime) values ('6/12/2010 10:10am','6/12/2010 10:15am')
DECLARE @StartDate datetime
,@EndDate datetime
SELECT @StartDate='6/12/2010'
,@EndDate='6/13/2010'
;with AllDates AS
(
SELECT @StartDate AS DateOf
UNION ALL
SELECT DATEADD(second,1,DateOf) AS DateOf
FROM AllDates
WHERE DateOf<@EndDate
)
SELECT
a.DateOf,COUNT(c.callid) AS CountOfCalls
FROM AllDates a
INNER JOIN @Calls c ON a.DateOf>=c.starttime and a.DateOf<=c.endtime
GROUP BY a.DateOf
ORDER BY 2 DESC
OPTION (MAXRECURSION 0)
ВЫВОД:
DateOf CountOfCalls
----------------------- ------------
2010-06-12 10:10:00.000 3
2010-06-12 10:10:01.000 3
2010-06-12 10:10:02.000 3
2010-06-12 10:10:03.000 3
2010-06-12 10:10:04.000 3
2010-06-12 10:10:05.000 3
2010-06-12 10:10:06.000 3
2010-06-12 10:10:07.000 3
2010-06-12 10:10:08.000 3
2010-06-12 10:10:09.000 3
2010-06-12 10:10:10.000 3
2010-06-12 10:10:11.000 3
2010-06-12 10:10:12.000 3
2010-06-12 10:10:13.000 3
2010-06-12 10:10:14.000 3
2010-06-12 10:10:15.000 3
2010-06-12 10:10:16.000 3
2010-06-12 10:10:17.000 3
2010-06-12 10:10:18.000 3
2010-06-12 10:10:19.000 3
2010-06-12 10:10:20.000 3
2010-06-12 10:10:21.000 3
2010-06-12 10:10:22.000 3
2010-06-12 10:10:23.000 3
2010-06-12 10:10:24.000 3
2010-06-12 10:10:25.000 3
2010-06-12 10:10:26.000 3
2010-06-12 10:10:27.000 3
....
добавить TOP 1
или поместить этот запрос в производную таблицу и, если необходимо, дополнительно активировать ее.
Ответ 3
Единственный практический метод, о котором я могу думать, заключается в следующем:
Разделите период, который вы хотите проанализировать в произвольных "ведрах", скажем, 24 часа в час в течение дня. Для каждого ведра подсчитывайте, сколько звонков начинаются или заканчиваются между началом или концом интервала
Обратите внимание, что ограничение в 1 час не является правилом с жесткими и быстрыми темпами. Вы можете сделать это короче или дольше, в зависимости от того, насколько точно вы хотите, чтобы расчет был.
Вы можете сделать фактическую "длину" ведра функцией средней продолжительности вызова.
Итак, допустим, что ваш средний вызов составляет 3 минуты. Если это не слишком дорого с точки зрения расчетов, используйте ведра, которые в 3 раза дольше, чем ваш средний вызов (9 минут), это должно быть достаточно гранулированным, чтобы дать точные результаты.
Ответ 4
Отказ от ответственности: я пишу свой ответ на основе (excelent) следующего сообщения:
http://sqlmag.com/t-sql/calculating-concurrent-sessions-part-3 (также рекомендуются части 1 и 2)
Первое, что нужно понять здесь, заключается в том, что большинство современных решений, найденных в Интернете, могут иметь в основном две проблемы
- Результат не является правильным ответом (например, если диапазон A перекрывается с B и C, но B dosen't перекрывается с C, они считаются 3 перекрывающимися диапазонами).
- Способ вычисления очень неэффективен (потому что O (n ^ 2) и/или они засекречивают каждую секунду за период)
Общая проблема с производительностью в таких решениях, как предложенная Unreasons, - это решение для каждого решения, для каждого вызова вам нужно проверить все остальные вызовы, если они перекрываются.
существует алгоритмическое линейное общее решение, в котором перечислены все "события" (начальный и конечный вызов), упорядоченные по дате, и добавьте 1 для начала и вычитания 1 для зависания и помните о макс. Это может быть легко реализовано с помощью курсора (решение, предложенное Hafhor, похоже, таким образом), но курсоры не являются наиболее эффективными способами решения проблем.
В приведенной статье есть превосходные примеры, разные решения, сравнение их производительности. Предлагаемое решение:
WITH C1 AS
(
SELECT starttime AS ts, +1 AS TYPE,
ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
FROM Calls
UNION ALL
SELECT endtime, -1, NULL
FROM Calls
),
C2 AS
(
SELECT *,
ROW_NUMBER() OVER( ORDER BY ts, TYPE) AS start_or_end_ordinal
FROM C1
)
SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
Объяснение
предположим, что этот набор данных
+-------------------------+-------------------------+
| starttime | endtime |
+-------------------------+-------------------------+
| 2009-01-01 00:02:10.000 | 2009-01-01 00:05:24.000 |
| 2009-01-01 00:02:19.000 | 2009-01-01 00:02:35.000 |
| 2009-01-01 00:02:57.000 | 2009-01-01 00:04:04.000 |
| 2009-01-01 00:04:12.000 | 2009-01-01 00:04:52.000 |
+-------------------------+-------------------------+
Это способ реализовать с запросом ту же идею, добавляя 1 для каждого запуска вызова и вычитая 1 для каждого окончания.
SELECT starttime AS ts, +1 AS TYPE,
ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
FROM Calls
эта часть C1 CTE будет принимать каждое время начала каждого вызова и номер его
+-------------------------+------+---------------+
| ts | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 |
| 2009-01-01 00:02:57.000 | 1 | 3 |
| 2009-01-01 00:04:12.000 | 1 | 4 |
+-------------------------+------+---------------+
Теперь этот код
SELECT endtime, -1, NULL
FROM Calls
Сгенерирует все "длительности" без нумерации строк
+-------------------------+----+------+
| endtime | | |
+-------------------------+----+------+
| 2009-01-01 00:02:35.000 | -1 | NULL |
| 2009-01-01 00:04:04.000 | -1 | NULL |
| 2009-01-01 00:04:52.000 | -1 | NULL |
| 2009-01-01 00:05:24.000 | -1 | NULL |
+-------------------------+----+------+
Теперь, когда UNION будет иметь полное определение C1 CTE, вы будете иметь смешанные таблицы
+-------------------------+------+---------------+
| ts | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 |
| 2009-01-01 00:02:57.000 | 1 | 3 |
| 2009-01-01 00:04:12.000 | 1 | 4 |
| 2009-01-01 00:02:35.000 | -1 | NULL |
| 2009-01-01 00:04:04.000 | -1 | NULL |
| 2009-01-01 00:04:52.000 | -1 | NULL |
| 2009-01-01 00:05:24.000 | -1 | NULL |
+-------------------------+------+---------------+
C2 вычисляется сортировка и нумерация C1 с новым столбцом
C2 AS
(
SELECT *,
ROW_NUMBER() OVER( ORDER BY ts, TYPE) AS start_or_end_ordinal
FROM C1
)
+-------------------------+------+-------+--------------+
| ts | TYPE | start | start_or_end |
+-------------------------+------+-------+--------------+
| 2009-01-01 00:02:10.000 | 1 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 | 2 |
| 2009-01-01 00:02:35.000 | -1 | NULL | 3 |
| 2009-01-01 00:02:57.000 | 1 | 3 | 4 |
| 2009-01-01 00:04:04.000 | -1 | NULL | 5 |
| 2009-01-01 00:04:12.000 | 1 | 4 | 6 |
| 2009-01-01 00:04:52.000 | -1 | NULL | 7 |
| 2009-01-01 00:05:24.000 | -1 | NULL | 8 |
+-------------------------+------+-------+--------------+
И там, где происходит волшебство, в любой момент результатом #start - #ends является количество одновременных вызовов в данный момент.
для каждого типа = 1 (начало события) мы имеем значение #start в третьем столбце. и у нас также есть #start + #end (в 4-м столбце)
#start_or_end = #start + #end
#end = (#start_or_end - #start)
#start - #end = #start - (#start_or_end - #start)
#start - #end = 2 * #start - #start_or_end
поэтому в SQL:
SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
В этом случае с предлагаемым набором вызовов результат равен 2.
В предлагаемой статье есть небольшое улучшение, чтобы иметь сгруппированный результат, например, службой или "телефонной компанией" или "центральным телефоном", и эта идея также может использоваться для группировки, например, по временному интервалу и максимальный concurrency час за часом в данный день.
Ответ 5
SELECT COUNT(*) FROM calls
WHERE '2010-06-15 15:00:00' BETWEEN calls.starttime AND calls.endtime
и повторяйте это каждую секунду.
Ответ 6
-- assuming calls table with columns starttime and endtime
declare @s datetime, @e datetime;
declare @t table(d datetime);
declare c cursor for select starttime,endtime from calls order by starttime;
open c
while(1=1) begin
fetch next from c into @s,@e
if @@FETCH_STATUS<>0 break;
update top(1) @t set [email protected] where d<[email protected];
if @@ROWCOUNT=0 insert @t(d) values(@e);
end
close c
deallocate c
select COUNT(*) as MaxConcurrentCalls from @t