Eu acho que esgotei os limites do meu conhecimento no SQL Server neste ....
Para encontrar uma lacuna no SQL server (o que o código C # faz) e você não se importa com o início ou o término de lacunas (aquelas antes da primeira inicialização ou após a última conclusão), a seguinte consulta (ou variantes) é a o mais rápido que pude encontrar:
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
O que funciona muito bem, pois para cada conjunto de início e término, você pode tratar o início e o final como sequências separadas, compensar o final em um e as lacunas são mostradas.
por exemplo, take (S1, F1), (S2, F2), (S3, F3) e faça o pedido como: {S1, S2, S3, null} e {null, F1, F2, F3} Em seguida, compare a linha n com a linha n em cada conjunto, e as lacunas são onde o valor do conjunto F é menor que o valor do conjunto S ... o problema é que, no SQL Server, não há como associar ou comparar dois conjuntos separados apenas na ordem dos valores em o conjunto ... daí o uso da função row_number para nos permitir mesclar com base apenas no número da linha ... mas não há como dizer ao SQL Server que esses valores são únicos (sem inseri-los em uma tabela var com um índice) eu tentei), então acho que a junção de mesclagem é menor que a ideal? (embora difícil de provar quando é mais rápido do que qualquer outra coisa que eu poderia fazer)
Consegui obter soluções usando as funções 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
(que, a propósito, não garanto os resultados - parece funcionar, mas acho que depende do StartedAt estar em ordem na tabela Tarefas ... e foi mais lento)
Usando alteração de soma:
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
(sem surpresa, também mais lento)
Eu até tentei uma função agregada CLR (para substituir a soma - era mais lenta que a soma e dependia de row_number () para manter a ordem dos dados) e CLR uma função com valor de tabela (para abrir dois conjuntos de resultados e comparar valores baseados puramente em sequência) ... e também foi mais lento. Eu bati minha cabeça tantas vezes nas limitações de SQL e CLR, tentando muitos outros métodos ...
E para quê?
Executando na mesma máquina e cuspindo os dados C # e SQL filtrados em um arquivo (conforme o código C # original), os tempos são praticamente os mesmos ... aproximadamente 2 segundos para os dados de 1 intervalo (C # geralmente mais rápido ), 8 a 10 segundos para o conjunto de dados com vários espaços (SQL geralmente mais rápido).
NOTA : Não use o SQL Server Development Environment para comparação de tempo, pois a exibição na grade leva tempo. Conforme testado com o SQL 2012, VS2010, .net 4.0 Perfil do cliente
Apontarei que ambas as soluções realizam praticamente a mesma classificação de dados no servidor SQL, portanto a carga do servidor para a busca-busca será semelhante, independentemente da solução usada, a única diferença é o processamento no cliente (e não no servidor) e a transferência pela rede.
Eu não sei qual pode ser a diferença ao particionar por diferentes membros da equipe, talvez, ou quando você precisar de dados extras com as informações de lacunas (embora eu não consiga pensar em outra coisa senão uma identificação de equipe), ou claro, se existe uma conexão de dados lenta entre o servidor SQL e a máquina cliente (ou um cliente lento ) ... Também não fiz uma comparação de tempos de bloqueio, problemas de contenção ou problemas de CPU / REDE para vários usuários ... não sei qual é mais provável que seja um gargalo neste caso.
O que eu sei é que sim, o SQL Server não é bom nesse tipo de comparação de conjuntos e, se você não escrever a consulta corretamente, pagará caro.
É mais fácil ou mais difícil do que escrever a versão C #? Não tenho certeza absoluta de que a solução total em execução Change +/- 1 também não é totalmente intuitiva, e eu, mas não é a primeira solução para a qual um graduado comum chegaria ... uma vez feito, é fácil copiar, mas é preciso discernimento para escrever em primeiro lugar ... o mesmo pode ser dito para a versão SQL. Qual é mais difícil? Qual é mais robusto para dados não autorizados? Qual tem mais potencial para operações paralelas? Realmente importa quando a diferença é tão pequena em comparação com o esforço de programação?
Uma última nota; há uma restrição não declarada nos dados - o StartedAt deve ser menor que o FinishedAt, ou você obterá resultados ruins.