Uma solução T-SQL para lacunas e ilhas pode ser executada mais rapidamente do que uma solução C # em execução no cliente?
Para ser específico, vamos fornecer alguns dados de teste:
CREATE TABLE dbo.Numbers
(
n INT NOT NULL
PRIMARY KEY
) ;
GO
INSERT INTO dbo.Numbers
( n )
VALUES ( 1 ) ;
GO
DECLARE @i INT ;
SET @i = 0 ;
WHILE @i < 21
BEGIN
INSERT INTO dbo.Numbers
( n
)
SELECT n + POWER(2, @i)
FROM dbo.Numbers ;
SET @i = @i + 1 ;
END ;
GO
CREATE TABLE dbo.Tasks
(
StartedAt SMALLDATETIME NOT NULL ,
FinishedAt SMALLDATETIME NOT NULL ,
CONSTRAINT PK_Tasks PRIMARY KEY ( StartedAt, FinishedAt ) ,
CONSTRAINT UNQ_Tasks UNIQUE ( FinishedAt, StartedAt )
) ;
GO
INSERT INTO dbo.Tasks
( StartedAt ,
FinishedAt
)
SELECT DATEADD(MINUTE, n, '20100101') AS StartedAt ,
DATEADD(MINUTE, n + 2, '20100101') AS FinishedAt
FROM dbo.Numbers
WHERE ( n < 500000
OR n > 500005
)
GO
Este primeiro conjunto de dados de teste possui exatamente uma lacuna:
SELECT StartedAt ,
FinishedAt
FROM dbo.Tasks
WHERE StartedAt BETWEEN DATEADD(MINUTE, 499999, '20100101')
AND DATEADD(MINUTE, 500006, '20100101')
O segundo conjunto de dados de teste possui intervalos de 2M -1, um intervalo entre cada dois intervalos adjacentes:
TRUNCATE TABLE dbo.Tasks;
GO
INSERT INTO dbo.Tasks
( StartedAt ,
FinishedAt
)
SELECT DATEADD(MINUTE, 3*n, '20100101') AS StartedAt ,
DATEADD(MINUTE, 3*n + 2, '20100101') AS FinishedAt
FROM dbo.Numbers
WHERE ( n < 500000
OR n > 500005
)
GO
Atualmente, estou executando o 2008 R2, mas as soluções de 2012 são muito bem-vindas. Publiquei minha solução C # como resposta.
O seguinte código C # resolve o problema:
Este código chama este procedimento armazenado:
Ele localiza e imprime um intervalo em intervalos de 2 milhões nos seguintes horários, cache quente:
Ele localiza e imprime intervalos de 2M-1 em intervalos de 2M nos seguintes horários, cache quente:
Esta é uma solução muito simples - levei 10 minutos para desenvolver. Um recém-formado pode fazer isso. No lado do banco de dados, o plano de execução é uma junção de mesclagem trivial que usa muito pouca CPU e memória.
Edit: para ser realista, estou executando cliente e servidor em caixas separadas.
fonte
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:
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:
(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:
(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.
fonte
Aqui está uma solução que é executada em 4 segundos.
fonte