Myślę, że wyczerpałem granice mojej wiedzy na temat serwera SQL na tym ...
Aby znaleźć lukę w serwerze SQL (co robi kod C #), a nie przejmujesz się początkowymi lub końcowymi lukami (tymi przed pierwszym uruchomieniem lub po ostatnim zakończeniu), to następujące zapytanie (lub warianty) to najszybciej mogłem znaleźć:
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
To działa, choć nieznacznie z ręki, że dla każdego zestawu start-meta możesz traktować początek i koniec jako osobne sekwencje, przesunąć wykończenie o jeden i pokazywane są przerwy.
np. weź (S1, F1), (S2, F2), (S3, F3) i uporządkuj jako: {S1, S2, S3, null} i {null, F1, F2, F3} Następnie porównaj wiersz n do rzędu n w każdym zestawie, a luki są tam, gdzie wartość zestawu F jest mniejsza niż wartość zestawu S ... Problemem myślę, że w serwerze SQL nie ma możliwości połączenia lub porównania dwóch oddzielnych zestawów wyłącznie w kolejności wartości w zestaw ... stąd użycie funkcji numer_wiersza, aby umożliwić nam scalanie oparte wyłącznie na numerze wiersza ... ale nie ma sposobu, aby powiedzieć serwerowi SQL, że te wartości są unikalne (bez wstawiania ich do zmiennej var z indeksem na to - co trwa dłużej - próbowałem), więc myślę, że łączenie scalające jest mniej niż optymalne? (choć trudno udowodnić, kiedy jest szybszy niż cokolwiek innego, co mógłbym zrobić)
Udało mi się uzyskać rozwiązania za pomocą funkcji 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
(które, nawiasem mówiąc, nie gwarantuję wyników - wydaje się, że działa, ale myślę, że polega na tym, że StartedA jest w porządku w tabeli Zadań ... i było wolniej)
Za pomocą zmiany sumy:
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
(bez zaskoczenia, również wolniej)
Próbowałem nawet funkcji agregującej CLR (w celu zastąpienia sumy - była wolniejsza od sumy i polegałem na row_number () w celu zachowania kolejności danych), a CLR to funkcja o wartościach w tabeli (aby otworzyć dwa zestawy wyników i porównać wartości oparte wyłącznie sekwencyjnie) ... i to też było wolniejsze. Wielokrotnie waliłem głową w ograniczenia SQL i CLR, próbując wielu innych metod ...
I po co?
Działa na tym samym komputerze i pluje zarówno dane C #, jak i dane filtrowane SQL do pliku (zgodnie z oryginalnym kodem C #), czasy są praktycznie takie same .... około 2 sekund dla danych 1 luki (C # zwykle szybciej ), 8–10 sekund dla zestawu danych z wieloma przerwami (SQL zwykle szybciej).
UWAGA : Nie używaj środowiska programistycznego SQL Server do porównywania czasu, ponieważ jego wyświetlanie do siatki wymaga czasu. Testowany z SQL 2012, VS2010, profil klienta .net 4.0
Zwrócę uwagę, że oba rozwiązania wykonują prawie takie same sortowanie danych na serwerze SQL, więc obciążenie serwera dla pobierania i sortowania będzie podobne, niezależnie od tego, które rozwiązanie zastosujesz, jedyną różnicą jest przetwarzanie na kliencie (a nie na serwerze) oraz transfer przez sieć.
Nie wiem, jaka może być różnica podczas partycjonowania przez różnych członków personelu, lub kiedy możesz potrzebować dodatkowych danych z informacjami o luce (chociaż nie mogę wymyślić nic innego niż identyfikator personelu), lub oczywiście, jeśli jest powolne połączenie danych pomiędzy serwerem SQL i komputerze klienta (lub powolnego klienta) ... ani nie zrobiłem porównanie czasów blokady lub problemów rywalizacji lub CPU / problemów sieciowych dla wielu użytkowników ... Więc ja nie wiem, który z nich może być w tym przypadku wąskim gardłem.
Wiem, że tak, serwer SQL nie jest dobry w tego rodzaju zestawieniach porównań, a jeśli nie napiszesz poprawnie zapytania, słono za to zapłacisz.
Czy jest to łatwiejsze czy trudniejsze niż pisanie wersji C #? Nie jestem do końca pewien, czy zmiana +/- 1, uruchamianie kompleksowego rozwiązania, nie jest również całkowicie intuicyjna, ale ja, ale nie jest to pierwsze rozwiązanie, do którego wpadłby przeciętny absolwent ... po wykonaniu można łatwo skopiować, ale przede wszystkim potrzeba wglądu ... to samo można powiedzieć o wersji SQL. Co jest trudniejsze? Który jest bardziej odporny na nieuczciwe dane? Który ma większy potencjał dla operacji równoległych? Czy to naprawdę ma znaczenie, gdy różnica jest tak mała w porównaniu do wysiłku programowania?
Ostatnia uwaga; istnieje nieokreślone ograniczenie danych - wartość StartedAt musi być mniejsza niż wartość FinishedAt, w przeciwnym razie otrzymasz złe wyniki.