Я думаю, что исчерпал пределы моих знаний в SQL-сервере на этом ....
Чтобы найти пробел в SQL-сервере (что делает код C #), и вам не нужны начальные или конечные пробелы (те, которые находятся до первого начала или после последнего конца), тогда следующий запрос (или варианты) является быстрее всего я смог найти:
SELECT e.FinishedAt as GapStart, s.StartedAt as GapEnd
FROM
(
SELECT StartedAt, ROW_NUMBER() OVER (ORDER BY StartedAt) AS rn
FROM dbo.Tasks
) AS s
INNER JOIN
(
SELECT FinishedAt, ROW_NUMBER() OVER (ORDER BY FinishedAt) + 1 AS rn
FROM dbo.Tasks
) AS e ON e.rn = s.rn and s.StartedAt > e.FinishedAt
Это работает, хотя и незначительно, но для каждого набора начала и конца вы можете рассматривать начало и конец как отдельные последовательности, сдвигать окончание на единицу и показывать промежутки.
например, возьмите (S1, F1), (S2, F2), (S3, F3) и закажите как: {S1, S2, S3, null} и {null, F1, F2, F3} Затем сравните строку n со строкой n в каждом наборе, и пропуски, где значение F набора меньше, чем значение S набора ... проблема, я думаю, в том, что в SQL-сервере нет никакого способа объединить или сравнить два отдельных набора только по порядку значений в набор ... отсюда использование функции row_number, позволяющей нам объединять, основываясь исключительно на номере строки ... но нет никакого способа сообщить SQL-серверу, что эти значения уникальны (без вставки их в таблицу var с индексом на это - что занимает больше времени - я пробовал), так что я думаю, что объединение слиянием менее чем оптимально? (хотя трудно доказать, когда это быстрее, чем что-либо еще, что я мог сделать)
Я смог получить решения, используя функции LAG / LEAD:
select * from
(
SELECT top (100) percent StartedAt, FinishedAt, LEAD(StartedAt, 1, null) OVER (Order by FinishedAt) as NextStart
FROM dbo.Tasks
) as x
where NextStart > FinishedAt
(что, кстати, я не гарантирую результатов - похоже, это работает, но я думаю, что полагается на StartedAt в порядке в таблице задач ... и это было медленнее)
Использование изменения суммы:
select * from
(
SELECT EventTime, Change, SUM(Change) OVER (ORDER BY EventTime, Change desc ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as RunTotal --, x.*
FROM
(
SELECT StartedAt AS EventTime, 1 AS Change
FROM dbo.Tasks
UNION ALL
SELECT FinishedAt AS EventTime, -1 AS Change
FROM dbo.Tasks
) AS TaskEvents
) as x
where x.RunTotal = 0 or (x.RunTotal = 1 and x.Change = 1)
ORDER BY EventTime, Change DESC
(не удивительно, также медленнее)
Я даже пытался использовать агрегатную функцию CLR (чтобы заменить сумму - она была медленнее суммы и использовала row_number () для сохранения порядка данных), а CLR - табличную функцию (чтобы открыть два набора результатов и сравнить значения на основе чисто по порядку) ... и это тоже было медленнее. Я много раз ломал голову над ограничениями SQL и CLR, пробуя многие другие методы ...
И для чего?
Работая на одной машине и выплевывая как данные C #, так и данные, отфильтрованные с помощью SQL, в файл (согласно исходному коду C #), время практически одинаковое .... примерно 2 секунды для данных с 1 разрывом (C # обычно быстрее ), 8-10 секунд для набора данных с несколькими промежутками (SQL обычно быстрее).
ПРИМЕЧАНИЕ . Не используйте среду разработки SQL Server для сравнения времени, так как для ее отображения в сетке требуется время. Как протестировано с SQL 2012, VS2010, .net 4.0 Профиль клиента
Я укажу, что оба решения выполняют практически одинаковую сортировку данных на сервере SQL, поэтому нагрузка на сервер для извлечения и выборки будет одинаковой, какое бы решение вы ни использовали, единственная разница заключается в обработке на клиенте (а не на сервере) и передача по сети.
Я не знаю, в чем разница, возможно, при разделении между разными сотрудниками, или когда вам могут понадобиться дополнительные данные с информацией о пропусках (хотя я не могу думать о многом другом, кроме идентификатора персонала), или, конечно, если есть медленное соединение для передачи данных между сервером SQL и клиентской машиной (или медленным клиентом) ... Также я сделал сравнение запираемых раз, или проблемы раздора, или CPU / вопросы сети для нескольких пользователей ... Так что я не знаю, какой из них, скорее всего, будет узким местом в этом случае.
Что я действительно знаю, так это то, что SQL-сервер не подходит для такого рода сравнений множеств, и если вы не напишете запрос правильно, вы заплатите за него дорого.
Это проще или сложнее, чем писать версию на C #? Я не совсем уверен, что решение Change +/- 1, работающее в целом, также не совсем интуитивно понятно, и я, но это не первое решение, к которому придет среднестатистический выпускник ... после этого его достаточно легко скопировать, но Прежде всего, нужно разобраться, чтобы написать ... То же самое можно сказать и о версии SQL. Что сложнее? Что является более надежным для мошеннических данных? Который имеет больший потенциал для параллельных операций? Действительно ли имеет значение, когда разница настолько мала по сравнению с усилиями по программированию?
Одна последняя заметка; существует необъявленное ограничение на данные - значение StartedAt должно быть меньше значения FinishedAt, иначе вы получите плохие результаты.