Penso di aver esaurito i limiti delle mie conoscenze in SQL Server su questo ....
Per trovare un gap nel server SQL (cosa fa il codice C #) e non ti interessa iniziare o terminare i gap (quelli prima del primo avvio o dopo l'ultimo fine), la seguente query (o varianti) è la più veloce che ho trovato:
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
Che funziona anche se leggermente di mano che per ogni set di inizio-fine, puoi trattare l'inizio e la fine come sequenze separate, sfalsare la fine di uno e gli spazi sono mostrati.
es. prendere (S1, F1), (S2, F2), (S3, F3) e ordinare come: {S1, S2, S3, null} e {null, F1, F2, F3} Quindi confrontare la riga n con la riga n in ogni set, e le lacune sono dove il valore del set F è inferiore al valore del set S ... il problema penso sia che nel server SQL non c'è modo di unire o confrontare due set separati puramente nell'ordine dei valori in il set ... quindi l'uso della funzione row_number per permetterci di unirci basandoci esclusivamente sul numero di riga ... ma non c'è modo di dire a SQL Server che questi valori sono univoci (senza inserirli in una tabella var con un indice su di esso - che richiede più tempo - l'ho provato), quindi penso che l'unione di unione sia meno che ottimale? (anche se difficile da dimostrare quando è più veloce di qualsiasi altra cosa che potrei fare)
Sono stato in grado di ottenere soluzioni utilizzando le funzioni 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
(che a proposito, non garantisco i risultati - sembra funzionare, ma penso che si basi su Started: essere in ordine nella tabella delle attività ... ed è stato più lento)
Utilizzando la modifica della somma:
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
(nessuna sorpresa, anche più lento)
Ho anche provato una funzione aggregata CLR (per sostituire la somma: era più lenta della somma e mi sono affidato a row_number () per mantenere l'ordine dei dati) e CLR una funzione con valori di tabella (per aprire due set di risultati e confrontare i valori in base puramente in sequenza) ... ed era anche più lento. Ho sbattuto la testa così tante volte su SQL e limitazioni CLR, provando molti altri metodi ...
E per cosa?
In esecuzione sullo stesso computer e sputando sia i dati C # sia i dati filtrati SQL in un file (come per il codice C # originale), i tempi sono praticamente gli stessi .... circa 2 secondi per i dati 1 gap (C # solitamente più veloce ), 8-10 secondi per il set di dati multi-gap (SQL in genere più veloce).
NOTA : non utilizzare l'ambiente di sviluppo di SQL Server per il confronto dei tempi, poiché la visualizzazione sulla griglia richiede tempo. Testato con SQL 2012, VS2010, profilo client .net 4.0
Sottolineerò che entrambe le soluzioni eseguono praticamente lo stesso ordinamento di dati sul server SQL, quindi il carico del server per il fetch-sort sarà simile, qualunque sia la soluzione utilizzata, l'unica differenza è l'elaborazione sul client (anziché sul server) e il trasferimento in rete.
Non so quale potrebbe essere la differenza nel partizionare da diversi membri dello staff, o quando potresti aver bisogno di dati extra con le informazioni sul gap (anche se non riesco a pensare a nient'altro che un ID personale), o ovviamente se c'è una connessione dati lenta tra il server SQL e la macchina client (o un client lento ) ... Né ho fatto un confronto tra tempi di blocco, problemi di contesa o problemi di CPU / RETE per più utenti ... Quindi non so quale sia più probabile che sia un collo di bottiglia in questo caso.
Quello che so, è sì, SQL Server non è bravo in questo tipo di confronti set, e se non scrivi la query nel modo giusto pagherai a caro prezzo.
È più facile o più difficile che scrivere la versione C #? Non sono del tutto sicuro, la modifica +/- 1, che esegue la soluzione totale non è nemmeno del tutto intuitiva, e io ma non è la prima soluzione a cui un laureato medio dovrebbe arrivare ... una volta fatto è abbastanza facile da copiare, ma ci vuole un po 'di comprensione per scrivere ... lo stesso si può dire per la versione SQL. Qual è più difficile? Qual è più robusto per i dati non autorizzati? Quale ha più potenziale per operazioni parallele? Importa davvero quando la differenza è così piccola rispetto allo sforzo di programmazione?
Un'ultima nota; esiste un vincolo non dichiarato sui dati: StartedAt deve essere inferiore a FinishedAt o si otterranno risultati negativi.