Ich glaube, ich habe die Grenzen meines Wissens in SQL Server in diesem Fall ausgeschöpft ...
Um eine Lücke in SQL Server zu finden (was der C # -Code tut) und sich nicht darum zu kümmern, Lücken zu starten oder zu beenden (vor dem ersten Start oder nach dem letzten Ende), ist die folgende Abfrage (oder Varianten) die am schnellsten konnte ich finden:
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
Dies funktioniert zwar geringfügig, aber für jeden Start-Ziel-Satz können Sie Start und Ziel als separate Sequenzen behandeln, das Ziel um eins versetzen und Lücken werden angezeigt.
Nehmen Sie z. B. (S1, F1), (S2, F2), (S3, F3) und ordnen Sie wie folgt: {S1, S2, S3, null} und {null, F1, F2, F3}. Vergleichen Sie dann Zeile n mit Zeile n In jedem Satz und in Lücken ist der F-Satzwert kleiner als der S-Satzwert. Ich denke, das Problem ist, dass es in SQL Server keine Möglichkeit gibt, zwei separate Sätze nur in der Reihenfolge der Werte in zu verbinden oder zu vergleichen the set ... daher die Verwendung der row_number-Funktion, um das Zusammenführen nur anhand der Zeilennummer zu ermöglichen ... aber es gibt keine Möglichkeit, SQL Server mitzuteilen, dass diese Werte eindeutig sind (ohne sie in eine Tabelle var mit einem Index einzufügen darauf - was länger dauert - ich habe es versucht), also denke ich, dass der Merge-Join nicht optimal ist? (obwohl schwer zu beweisen, wenn es schneller ist als alles andere, was ich tun könnte)
Mit den LAG / LEAD-Funktionen konnte ich Lösungen finden:
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
(was ich übrigens nicht garantiere, die Ergebnisse - es scheint zu funktionieren, aber ich denke, dass StartedAt in der Aufgabentabelle in Ordnung ist ... und es war langsamer)
Verwenden der Summenänderung:
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
(keine Überraschung, auch langsamer)
Ich habe sogar versucht, eine CLR-Aggregatfunktion (um die Summe zu ersetzen - sie war langsamer als die Summe und stützte sich auf row_number (), um die Reihenfolge der Daten beizubehalten) und CLR eine Tabellenwertfunktion (um zwei Ergebnismengen zu öffnen und Werte zu vergleichen, die ausschließlich darauf basieren auf Sequenz) ... und es war auch langsamer. Ich habe mich so oft mit SQL- und CLR-Einschränkungen beschäftigt und viele andere Methoden ausprobiert ...
Und wofür?
Wenn Sie auf demselben Computer ausgeführt werden und sowohl die C # -Daten als auch die SQL-gefilterten Daten in eine Datei (gemäß dem ursprünglichen C # -Code) spucken, sind die Zeiten praktisch gleich ... ungefähr 2 Sekunden für die 1-Lücken-Daten (C # normalerweise schneller ), 8-10 Sekunden für den Multi-Gap-Datensatz (SQL normalerweise schneller).
ANMERKUNG : Verwenden Sie die SQL Server-Entwicklungsumgebung nicht für den Zeitvergleich, da die Anzeige im Raster einige Zeit in Anspruch nimmt. Wie mit SQL 2012, VS2010, .net 4.0-Clientprofil getestet
Ich werde darauf hinweisen, dass beide Lösungen fast die gleiche Sortierung von Daten auf dem SQL Server durchführen, sodass die Serverlast für die Abrufsortierung ähnlich ist, je nachdem, welche Lösung Sie verwenden. Der einzige Unterschied besteht in der Verarbeitung auf dem Client (und nicht auf dem Server). und die Übertragung über das Netzwerk.
Ich weiß nicht, was der Unterschied sein könnte, wenn Sie möglicherweise nach verschiedenen Mitarbeitern partitionieren oder wenn Sie zusätzliche Daten mit den Lückeninformationen benötigen (obwohl mir nicht viel anderes als eine Mitarbeiter-ID einfällt), oder natürlich, wenn Es besteht eine langsame Datenverbindung zwischen dem SQL Server und dem Clientcomputer (oder einem langsamen Client) ... Ich habe auch keine Vergleichszeiten, Probleme mit Konflikten oder Probleme mit CPU / NETWORK für mehrere Benutzer verglichen ... Also ich Ich weiß nicht, welches in diesem Fall eher ein Engpass ist.
Was ich weiß, ist ja, SQL Server ist nicht gut in dieser Art von Set-Vergleichen, und wenn Sie die Abfrage nicht richtig schreiben, werden Sie teuer dafür bezahlen.
Ist es einfacher oder schwieriger als die C # -Version zu schreiben? Ich bin mir nicht ganz sicher, ob die Änderung +/- 1, die Gesamtlösung ausführt, auch nicht ganz intuitiv ist, und ich, aber es ist nicht die erste Lösung, zu der ein durchschnittlicher Absolvent kommen würde ... wenn sie fertig ist, ist es einfach genug, sie zu kopieren, aber Es braucht Einsicht, um überhaupt zu schreiben ... das Gleiche gilt für die SQL-Version. Welches ist schwieriger? Welches ist robuster gegenüber unerwünschten Daten? Welches hat mehr Potenzial für Parallelbetrieb? Ist es wirklich wichtig, wenn der Unterschied im Vergleich zum Programmieraufwand so gering ist?
Eine letzte Anmerkung; Es gibt eine nicht angegebene Einschränkung für die Daten - StartedAt muss kleiner als FinishedAt sein, sonst erhalten Sie schlechte Ergebnisse.