USE Model GO CREATE TABLE Numbers ( Number INT NOT NULL,CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) WITH FILLFACTOR = 100 ) INSERT INTO Numbers SELECT (a.Number * 256) + b.Number AS Number FROM ( SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255 ) a (Number),( SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255 ) b (Number) GO
Numbers tables are truly invaluable. I use them all of the time for string manipulation,simulating window functions,populating test tables with lots of data,eliminating cursor logic,and many other tasks that would be incredibly difficult without them.
但我完全不明白这些用途是什么 – 你能提供一些引人注目的具体例子,说明“数字表”在sql Server中为你节省了大量工作 – 以及为什么我们应该拥有它们?
解决方法@H_404_14@
当你需要预测“缺失数据”时,我已经看到了很多用途.例如.你有一个时间序列(例如访问日志),你想显示过去30天每天的点击次数(想想分析仪表板).如果您按天从…组中选择计数(…),您将获得每天的计数,但结果只会在您实际拥有至少一次访问权限的每一天中有一行.另一方面,如果你首先从你的数字表中选择一个天数表(选择dateadd(day,-number,today)作为数字的日期)然后你离开加入计数(或外部应用,无论你喜欢什么)然后在没有访问权限的日子里,你会得到一个0的计数结果.这只是一个例子.当然,有人可能会争辩说,仪表板的表示层可以处理丢失的日期而只是显示0,但是某些工具(例如SSRS)根本无法处理这个问题.
我见过的其他例子使用了类似的时间序列技巧(日期/时间/ – 数字)来进行各种窗口计算.通常,每当使用命令式语言时,您将使用具有众所周知的迭代次数的for循环,sql的声明性和集合性可以使用基于数字表的技巧.
顺便说一句,我觉得有必要提出一个事实,即使用数字表感觉就像命令式程序执行一样,不要陷入假设必要的谬误.让我举个例子:
int x;
for (int i=0;i<1000000;++i)
x = i;
printf("%d",x);
这个程序将输出999999,这是非常有保证的.
让我们使用数字表在sql中尝试相同的操作.首先创建一个包含1M个数字的表:
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',@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时刻(在所有数字都是聚集的主键之后!),诀窍被称为allocation order scan并且我没有偶然插入@ j * 1000 @i …你也可以冒险猜测说结果是因为parallelism而且有时可能是正确的答案.
有很多trolls under this bridge,我在On SQL Server boolean operator short-circuit和T-SQL functions do no imply a certain order of execution提到了一些
我见过的其他例子使用了类似的时间序列技巧(日期/时间/ – 数字)来进行各种窗口计算.通常,每当使用命令式语言时,您将使用具有众所周知的迭代次数的for循环,sql的声明性和集合性可以使用基于数字表的技巧.
顺便说一句,我觉得有必要提出一个事实,即使用数字表感觉就像命令式程序执行一样,不要陷入假设必要的谬误.让我举个例子:
int x; for (int i=0;i<1000000;++i) x = i; printf("%d",x);
这个程序将输出999999,这是非常有保证的.
让我们使用数字表在sql中尝试相同的操作.首先创建一个包含1M个数字的表:
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',@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时刻(在所有数字都是聚集的主键之后!),诀窍被称为allocation order scan并且我没有偶然插入@ j * 1000 @i …你也可以冒险猜测说结果是因为parallelism而且有时可能是正确的答案.
有很多trolls under this bridge,我在On SQL Server boolean operator short-circuit和T-SQL functions do no imply a certain order of execution提到了一些