Widziałem wiele zastosowań, kiedy trzeba wyświetlać „brakujące dane”. Na przykład. masz szereg czasowy (na przykład dziennik dostępu) i chcesz pokazać liczbę trafień dziennie w ciągu ostatnich 30 dni (panel kontrolny analizy). Jeśli to zrobisz select count(...) from ... group by day
, będziesz liczyć za każdy dzień, ale wynik będzie miał tylko wiersz dla każdego dnia, w którym faktycznie miałeś co najmniej jeden dostęp. Z drugiej strony, jeśli najpierw rzutujesz tabelę dni ze swojej tabeli liczb ( select dateadd(day, -number, today) as day from numbers
), a następnie opuściłeś łączenie z liczbami (lub aplikacją zewnętrzną, cokolwiek ci się podoba), otrzymasz wynik, który ma 0 dla liczby dni nie miał dostępu. To tylko przykład. Oczywiście można argumentować, że warstwa prezentacji na desce rozdzielczej poradzi sobie z brakującymi dniami i po prostu pokaże 0, ale niektóre narzędzia (np. SSRS) po prostu nie będą w stanie sobie z tym poradzić.
Inne przykłady, które widziałem, wykorzystywały podobne triki szeregów czasowych (data / godzina +/- liczba) do wykonywania wszelkiego rodzaju obliczeń w oknie. Ogólnie rzecz biorąc, ilekroć w imperatywnym języku użyłbyś pętli for ze znaną liczbą iteracji, deklaratywna i ustawiona natura SQL może użyć sztuczki opartej na tabeli liczb.
BTW, odczuwam potrzebę zwrócenia uwagi na fakt, że chociaż przy użyciu tabeli liczb wydaje się to bezwzględnie konieczne do wykonania procedury, nie popadaj w błąd, zakładając, że jest to konieczne. Podam przykład:
int x;
for (int i=0;i<1000000;++i)
x = i;
printf("%d",x);
Ten program wyświetli 999999, co jest prawie całkowicie gwarantowane.
Spróbujmy to samo w SQL Server, używając tabeli liczb. Najpierw utwórz tabelę zawierającą 1 000 000 liczb:
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
Teraz zróbmy „pętlę for”:
declare @x int;
select @x = number
from numbers with(nolock);
select @x as [@x];
Wynik to:
@x
-----------
88698
Jeśli masz teraz moment WTF (w końcu number
to klastrowany klucz podstawowy!), Sztuczka nazywa się skanowaniem kolejności alokacji, a ja nie wstawiłem @j*1000+@i
przypadkowo ... Mógłbyś również zaryzykować i powiedzieć, że wynik jest taki, ponieważ równoległość, która czasem może być poprawną odpowiedzią.
Pod tym mostem jest wiele trolli i wspomniałem o niektórych w zwarciu operatora logicznego On SQL Server, a funkcje T-SQL nie oznaczają określonej kolejności wykonywania