Eu já vi muitos usos quando você precisa projetar 'dados ausentes'. Por exemplo. você tem uma série temporal (um log de acesso, por exemplo) e deseja mostrar o número de ocorrências por dia nos últimos 30 dias (pense no painel de análise). Se você fizer um select count(...) from ... group by day
, receberá a contagem de todos os dias, mas o resultado terá apenas uma linha para cada dia em que você realmente teve pelo menos um acesso. Por outro lado, se você primeiro projeta uma tabela de dias a partir da tabela de números ( select dateadd(day, -number, today) as day from numbers
) e depois se junta às contagens (ou aplicação externa, o que quiser), obterá um resultado que tem 0 para a contagem nos dias em que você não teve acesso. isso é apenas um exemplo. Obviamente, pode-se argumentar que a camada de apresentação do seu painel poderia lidar com os dias ausentes e apenas mostrar um 0, mas algumas ferramentas (por exemplo, SSRS) simplesmente não serão capazes de lidar com isso.
Outros exemplos que eu já vi usaram truques de séries temporais semelhantes (data / hora +/- número) para fazer todos os tipos de cálculos de janela. Em geral, sempre que em uma linguagem imperativa você usasse um loop for com um número bem conhecido de iterações, a natureza declarativa e definida do SQL pode usar um truque baseado em uma tabela de números.
Aliás, sinto a necessidade de enfatizar o fato de que, embora usar uma tabela de números pareça uma execução processual imperativa, não caia na falácia de supor que ela é imperativa. Deixe-me dar um exemplo:
int x;
for (int i=0;i<1000000;++i)
x = i;
printf("%d",x);
Este programa produzirá 999999, o que é praticamente garantido.
Vamos tentar o mesmo no SQL Server, usando uma tabela numérica. Primeiro, crie uma tabela de 1.000.000 de números:
create table numbers (number int not null primary key);
go
declare @i int = 0
, @j int = 0;
set nocount on;
begin transaction
while @i < 1000
begin
set @j = 0;
while @j < 1000
begin
insert into numbers (number)
values (@j*1000+@i);
set @j += 1;
end
commit;
raiserror (N'Inserted %d*1000', 0, 0, @i)
begin transaction;
set @i += 1;
end
commit
go
Agora vamos fazer o 'for loop':
declare @x int;
select @x = number
from numbers with(nolock);
select @x as [@x];
O resultado é:
@x
-----------
88698
Se você está tendo um momento WTF (afinal, number
é a chave primária em cluster!), O truque é chamado de varredura da ordem de alocação e eu não a inseri @j*1000+@i
por acidente ... Você também pode arriscar um palpite e dizer que o resultado é porque paralelismo e que às vezes pode ser a resposta correta.
Existem muitas trolls sob essa ponte e mencionei algumas delas em curto-circuito no operador booleano do SQL Server e as funções T-SQL não implicam uma determinada ordem de execução