J'ai vu de nombreuses utilisations lorsque vous devez projeter des «données manquantes». Par exemple. vous avez une série chronologique (un journal d'accès par exemple) et vous souhaitez afficher le nombre de visites par jour au cours des 30 derniers jours (pensez au tableau de bord d'analyse). Si vous faites un, select count(...) from ... group by day
vous obtiendrez le nombre pour chaque jour, mais le résultat n'aura qu'une ligne pour chaque jour où vous avez eu au moins un accès. Par contre, si vous projetez d’abord une table de jours à partir de votre table de chiffres ( select dateadd(day, -number, today) as day from numbers
) et que vous quittez ensuite la liste des comptes eu aucun accès. Ceci n'est qu'un exemple. Bien sûr, on peut faire valoir que la couche de présentation de votre tableau de bord pourrait gérer les jours manquants et simplement afficher un 0, mais certains outils (par exemple, SSRS) ne pourront tout simplement pas gérer cela.
D'autres exemples que j'ai vus utilisaient des astuces similaires sur les séries chronologiques (date / heure + / - nombre) pour effectuer toutes sortes de calculs de fenêtre. En général, lorsque vous utilisez une boucle for avec un nombre bien connu d'itérations dans un langage impératif, la nature déclarative et définie de SQL peut utiliser une astuce basée sur une table de nombres.
BTW, je ressens le besoin de rappeler le fait que, même si utiliser un tableau de nombres, cela ressemble à une exécution procédurale impérative, ne tombez pas dans l'erreur de supposer qu'il est impératif. Laissez-moi vous donner un exemple:
int x;
for (int i=0;i<1000000;++i)
x = i;
printf("%d",x);
Ce programme produira 999999, ce qui est pratiquement garanti.
Essayons la même chose dans SQL Server, en utilisant une table numérique. Commencez par créer une table de 1 000 000 nombres:
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
Maintenant, faisons la boucle "for":
declare @x int;
select @x = number
from numbers with(nolock);
select @x as [@x];
Le résultat est:
@x
-----------
88698
Si vous avez maintenant un moment WTF (après tout, number
c'est la clé primaire en cluster!), L'astuce s'appelle le balayage d'ordre d'attribution et je ne l'ai pas insérée @j*1000+@i
par accident ... Vous pourriez aussi deviner et dire que le résultat est parce que parallélisme et que parfois peut être la bonne réponse.
Il y a beaucoup de trolls sous ce pont et j'en ai mentionné quelques-uns dans On SQL Server, un court-circuit d'opérateur booléen et des fonctions T-SQL n'impliquant pas un certain ordre d'exécution