Como inserir vários registros em uma tabela com valores sequenciais? Por exemplo, como podemos obter uma série de datas?

A resposta mais simples seria utilizar um cursor ou laço (loop), mas a execução de ambos é demasiadamente lenta para muitos registros. O desempenho máximo somente será obtido se a inserção for feita de uma só vez em um comando INSERT. Para isso, podemos usar uma tabela auxiliar com valores sequenciais de forma que possamos usar esses valores para derivar aqueles que necessitamos. Mas como obter tal tabela?

Uma das formas de unir velocidade e praticidade é criar uma tabela com um campo IDENTITY. Entretanto, não gosto da ideia de criar uma tabela física para auxiliar em operações completamente ortogonais às funcionalidades do sistema.

Opção fácil para poucos registros

A tabela MASTER..SPT_VALUES possui números sequenciais de 0 (zero) a 2047 e é nativa do SQL Server. O exemplo abaixo insere 2000 linhas com datas sequenciais de uma só vez:

INSERT INTO DATAS (DATA, DIADASEMANA)
SELECT DATEADD(d, NUMBER, @DATAINICIAL), DATEPART(DW,DATEADD(d, NUMBER, @DATAINICIAL))
FROM MASTER..SPT_VALUES
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 1999

Portanto, com essa tabela é possível fazer uma inserção em massa utilizando a coluna NUMBER e alguma função que calcule o valor do campo a partir do valor sequencial. No exemplo, a função DATEADD gera datas a partir da @DATAINICIAL, até 2000 dias à frente, e também armazena o dia da semana de cada data.

Porém, há uma questão sobre o uso de uma tabela do usuário MASTER, pois ela pode não ser acessível para você. E se não houver possibilidade de obter a devida permissão?

Utilizando uma tabela auxiliar em memória

Um outro método muito eficiente, mas que exige um pouco de Transact-SQL, é usar uma Variável do tipo TABLE. Confira o seguinte exemplo:

DECLARE @T TABLE (NUMBER INT)
DECLARE @I INT, @LIMITE INT

SET @I = 0
SET @LIMITE = 25000
WHILE @I < @LIMITE
BEGIN
    INSERT INTO @T (NUMBER) VALUES (@I)
    SET @I = @I + 1
END

Este código leva menos de 1 segundo para executar e cria uma tabela na memória com números de 0 a 24.999. Usando ainda o exemplo de geração de datas, podemos inserir 25 mil linhas de uma só vez, como mostra o novo exemplo abaixo:

DECLARE @DATAINICIO DATETIME
SET @DATAINICIO = '2001-01-01'
SELECT DATA, DATEPART(DW, DATA)
FROM (SELECT @DATAINICIO + NUMBER DATA FROM @T) T

Conclusão

Já usei várias vezes esse recurso para melhorar o desempenho de cálculos financeiros que envolvem parcelas, justo e vencimentos em feriados. Certa vez consegui diminuir o tempo de execução de uma procedure de cálculos, que levava 20 minutos para inserir 23 mil registros, para cerca de 20 segundos, gerando 150 mil registros.

Embora para alguns os exemplos acima possam soar como algum tipo de gambiarra, o conhecimento e a correta aplicação de tais recursos podem ser essenciaus quando as restrições de desempenho são prioridade.