Время наложения на сервере SQL - PullRequest
2 голосов
/ 10 января 2010

У меня такой стол

ColumnId Intime                   Outtime
1        01/02/2009 10.00.000    01/02/2009 20.00.0000  
2        01/02/2009 2.00.000    01/02/2009 2.00.0000 
3        01/02/2009 2.00.000    01/02/2009 5.00.0000 
4        01/02/2009 3.3.0.000    01/02/2009 5.00.0000 
5        01/02/2009 10.00.000    01/02/2009 22.00.0000 
6        01/02/2009 3.00.000    01/02/2009 4.00.0000 

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

Примечание: - В моей таблице миллионы записей.

например, в первом значении войдите в 10 и вышли из системы 20. и в 5 записали в 10 и вышли в 22, так что 5-е перекрывается с первым. В таблице отсутствуют индексы.

Пожалуйста, получите ответ на мой вопрос.

Мне нужен запрос для выполнения в SQL Server 2005

Ответы [ 5 ]

9 голосов
/ 10 января 2010

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

SELECT a.ColumnId
      ,a.InTime
      ,a.OutTime
      ,b.ColumnId AS OverlappingId
      ,b.InTime   AS OverlappingInTime
      ,b.OutTime  AS OverlappingOutTime
  FROM TimeTable  AS a
  JOIN TimeTable  AS b ON ((a.InTime  BETWEEN b.InTime AND b.OutTime)
                       OR  (a.OutTime BETWEEN b.InTime AND b.OutTime) 
                       OR  (a.InTime < b.InTime AND a.OutIme > b.OutTime))
                      AND  (a.ColumnId != b.ColumnId)

Но я действительно не уверен в производительности этого запроса в таблице с миллионами записей, как вы упомянули.

Отредактировано для добавления и отредактировано еще раз:

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

SELECT a.ColumnId 
      ,a.InTime 
      ,a.OutTime 
      ,b.ColumnId AS OverlappingId 
      ,b.InTime   AS OverlappingInTime 
      ,b.OutTime  AS OverlappingOutTime 
  FROM TimeTable  AS a 
  JOIN TimeTable  AS b ON ((a.InTime  BETWEEN b.InTime AND b.OutTime) 
                       OR  (a.OutTime BETWEEN b.InTime AND b.OutTime)) 
                      AND  (a.ColumnId != b.ColumnId) 

Использование начальных данных вопроса для теста:

+--------+------------------+------------------+
|ColumnId| InTime           | OutTime          |
+--------+------------------+------------------+
|      1 | 01/02/2009 10:00 | 01/02/2009 20:00 |   
|      2 | 01/02/2009  2:00 | 01/02/2009  2:00 |  
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |  
|      4 | 01/02/2009  3:03 | 01/02/2009  5:00 |  
|      5 | 01/02/2009 10:00 | 01/02/2009 22:00 |  
|      6 | 01/02/2009  3:00 | 01/02/2009  4:00 |  
+--------+------------------+------------------+

Запустив исходный запрос, мы получим следующий результат:

+--------+------------------+------------------+-------------+
|ColumnId| InTime           | OutTime          |OverlappingId|
+--------+------------------+------------------+-------------+
|      1 | 01/02/2009 10:00 | 01/02/2009 20:00 |           5 |
|      2 | 01/02/2009  2:00 | 01/02/2009  2:00 |           3 |
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |           2 |
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |           4 |
|      4 | 01/02/2009  3:03 | 01/02/2009  5:00 |           3 |
|      4 | 01/02/2009  3:03 | 01/02/2009  5:00 |           6 |
|      5 | 01/02/2009 10:00 | 01/02/2009 22:00 |           1 |
|      6 | 01/02/2009  3:00 | 01/02/2009  4:00 |           3 |
|      6 | 01/02/2009  3:00 | 01/02/2009  4:00 |           4 |
+--------+------------------+------------------+-------------+

Запустив обновленный запрос, мы получим следующий результат:

+--------+------------------+------------------+-------------+
|ColumnId| InTime           | OutTime          |OverlappingId|
+--------+------------------+------------------+-------------+
|      1 | 01/02/2009 10:00 | 01/02/2009 20:00 |           5 |
|      2 | 01/02/2009  2:00 | 01/02/2009  2:00 |           3 |
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |           2 |
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |           4 |
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |           6 | << missing row
|      4 | 01/02/2009  3:03 | 01/02/2009  5:00 |           3 |
|      4 | 01/02/2009  3:03 | 01/02/2009  5:00 |           6 |
|      5 | 01/02/2009 10:00 | 01/02/2009 22:00 |           1 |
|      6 | 01/02/2009  3:00 | 01/02/2009  4:00 |           3 |
|      6 | 01/02/2009  3:00 | 01/02/2009  4:00 |           4 |
+--------+------------------+------------------+-------------+

Да, некоторые идентификаторы повторяются, но это потому, что они перекрываются разными записями.

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

Некоторые люди предлагают использовать a.ColumnId < b.ColumnId или a.ColumnId > b.ColumnId, чтобы избежать повторения, однако, это все равно не работает, потому что, если бы мы сделали первое сравнение, мы получили бы следующий результат:

+--------+------------------+------------------+-------------+
|ColumnId| InTime           | OutTime          |OverlappingId|
+--------+------------------+------------------+-------------+
|      1 | 01/02/2009 10:00 | 01/02/2009 20:00 |           5 |
|      2 | 01/02/2009  2:00 | 01/02/2009  2:00 |           3 |
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |           4 |
|      3 | 01/02/2009  2:00 | 01/02/2009  5:00 |           6 | 
|      4 | 01/02/2009  3:03 | 01/02/2009  5:00 |           6 |
+--------+------------------+------------------+-------------+

Если вы заметили, что в результатах указаны все 6 строк данных примера, хотя в нем всего 5 строк. Я полагаю, что с этими данными, когда все строки перекрывают друг друга в той или иной точке, количество перекрывающихся строк равно 6.

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

SELECT COUNT (DISTINCT a.ColumnId)
  FROM TimeTable  AS a
  JOIN TimeTable  AS b ON ((a.InTime  BETWEEN b.InTime AND b.OutTime)
                       OR  (a.OutTime BETWEEN b.InTime AND b.OutTime) 
                       OR  (a.InTime < b.InTime AND a.OutIme > b.OutTime))
                      AND  (a.ColumnId != b.ColumnId)

Возвращает счетчик всех 6 строк.

5 голосов
/ 10 января 2010

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

select
    aa.ColumnId as ColumnIdA, aa.InTime as InTimeA, aa.OutTime as OutTimeA,
    bb.ColumnId as ColumnIdB, bb.InTime as InTimeB, bb.OutTime as OutTimeB
from
    MyTable aa
    join
    MyTable bb on aa.ColumnId < bb.ColumnId
where
    aa.InTime < bb.OutTime
    and
    aa.OutTime > bb.InTime

Нужно быть осторожным в определении "перекрытия". Я предполагаю, что если первый период - с 3:00 до 4:00, а второй период - с 4:00 до 5:00, то эти диапазоны не перекрываются. Если кто-то действительно хочет, чтобы этот случай считался перекрытием, измените < -to- <= и > -to- >= в предложении where.

Производительность пропорциональна квадрату количества строк. Более быстрые решения возможны для больших наборов данных, но гораздо более сложны, чем этот.

0 голосов
/ 27 ноября 2010

Вы можете использовать функцию OVERLAPS, если SQL соответствует ANSI 2003. примечание t1.c1

SEL *
FROM TimeTable AS t1,TimeTable AS t2
WHERE (t1.Intime,t1.Outtime) OVERLAPS (t2.Intime,t2.Outtime)
AND t1.ColumnId < t2.ColumnId
ORDER BY 1;
0 голосов
/ 10 января 2010
SELECT  T1. ColumnId, T1.Intime, T1.OutTime
FROM    T1, T2
WHERE    1 =1 
AND (    T2.Intime  BETWEEN T1.Intime AND T1.OutTime
      OR T2.OutTime BETWEEN T1.Intime AND T1.OutTime )
        AND T1.ColumnId <> T2.ColumnId
0 голосов
/ 10 января 2010
Select T1.*,T2.*
From Table1 T1
Inner Join Table1 T2 ON ((T1.InTime >= T2.InTime AND T1.OutTime > T2.InTime)
                         OR (T2.InTime >= T1.InTime AND T2.OutTime > T1.InTime)) 
                        AND (T1.ColumnId != T2.ColumnId)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...