Я бачив багато застосувань, коли потрібно спроектувати "відсутні дані". Напр. у вас є часовий ряд (наприклад, журнал доступу), і ви хочете показати кількість звернень на день за останні 30 днів (подумайте на інформаційній панелі аналітики). Якщо ви займаєтесь, select count(...) from ... group by day
ви отримуватимете підрахунок за кожен день, але результат матиме рядок лише за кожен день, коли ви фактично мали хоча б один доступ. З іншого боку, якщо ви спочатку спроектуєте таблицю днів із таблиці своїх цифр ( select dateadd(day, -number, today) as day from numbers
), а потім ви з’єднаєтесь з підрахунками (або зовнішньою заявою, що б вам не здалося), тоді ви отримаєте результат, який має 0 для підрахунку за дні, які ви не мали доступу. Це лише один приклад. Звичайно, можна стверджувати, що презентаційний шар вашої інформаційної панелі може обробляти пропущені дні та просто показувати 0, але деякі інструменти (наприклад, SSRS) просто не зможуть впоратися з цим.
Інші приклади, за якими я бачив, використовували подібні трюки часових рядів (дата / час +/- число) для виконання всіх видів розрахунків у вікні. Взагалі, щоразу, коли в обов'язковій мові ви використовуєте цикл із добре відомою кількістю ітерацій, декларативний та заданий характер SQL може використовувати хитрість на основі таблиці чисел.
До речі, я відчуваю необхідність зауважити той факт, що хоч за допомогою таблиці цифр це відчувається як імперативне процедурне виконання, але не потрапляє в помилковість вважати, що це обов'язково. Наведу приклад:
int x;
for (int i=0;i<1000000;++i)
x = i;
printf("%d",x);
Ця програма виведе 999999, що майже гарантовано.
Давайте спробуємо те ж саме в SQL Server, використовуючи таблицю номерів. Спочатку створіть таблицю з 1 000 000 чисел:
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
Тепер давайте зробимо "for цикл":
declare @x int;
select @x = number
from numbers with(nolock);
select @x as [@x];
Результат:
@x
-----------
88698
Якщо у вас зараз є момент WTF (адже number
це кластеризований первинний ключ!), Трюк називається скануванням розпорядження розподілу, і я не вставив @j*1000+@i
випадково ... Ви також могли задуматися і сказати, що результат полягає в тому, що паралелізм і що іноді може бути правильною відповіддю.
Під цим мостом є багато тролів, і я згадував про деякі на короткому замиканні булевого оператора на SQL Server, а функції T-SQL не передбачають певного порядку виконання