T-SQL:生成数字组合并保存到表中

Posted

技术标签:

【中文标题】T-SQL:生成数字组合并保存到表中【英文标题】:T-SQL : generate combination of numbers and save into table 【发布时间】:2020-09-08 21:03:32 【问题描述】:

我的查询可以生成 1 到 90 范围内 6 个数字的所有可能组合。

但是它在一段时间后停止工作,产生以下错误:

执行批处理时出错。错误消息是:引发了“System.OutOfMemoryException”类型的异常。

有没有办法解决这个问题以及如何将结果保存到表(不是临时表)?

WITH Numbers(N) AS 
(
    SELECT number
    FROM master..spt_values
    WHERE type = 'P'
      AND number BETWEEN 1 AND 90
)
SELECT *
FROM Numbers N1
JOIN Numbers N2 ON N2.N > N1.N
JOIN Numbers N3 ON N3.N > N2.N
JOIN Numbers N4 ON N4.N > N3.N
JOIN Numbers N5 ON N5.N > N4.N
JOIN Numbers N6 ON N6.N > N5.N

【问题讨论】:

你想做什么?这将生成数以亿计的行。 我正在尝试获得一张包含 90 种组合中所有可能的 6 种的表格 【参考方案1】:

评论太长了。

您的查询生成的行数实在是太多了。 1 到 90 个 evalue 之间的 6 个数字的所有可能组合为 90 ^ 6 行。

select power(cast(90 as real), 6) res

|          res |
| -----------: |
| 531441000000 |

超过 5000 亿行:显然,这会耗尽数据库服务器的资源。向它扔硬件不太可能使这成为可能。你应该重新考虑你想要做什么。

【讨论】:

。 .实际上,没有重复,元组是唯一的,所以它“只有”622,614,630(90*89*88*87*86​​85/6!)。 @GordonLinoff:我评论了问题陈述而不是查询本身 - 实际上,它生成的行更少(不知道 OP 实际上想要什么)。我想 6 亿仍然可以说是压倒性的…… 上次我看到有人试图这样做是为了生成用于彩票目的的所有组合¯\_(ツ)_/¯【参考方案2】:

您可以尝试一次填充您的表格,在每次迭代时将第一列的值增加 1。

先决条件:

    决定列的数据类型。这些值在 1-90 之间,它们适合 tinyint(1 字节)而不是 null。 6 列 = 6 个字节 创建唯一的聚集索引,因为列的组合是唯一的。一个堆会为 RID 增加 8 字节的开销,为唯一值增加 4 字节的非唯一聚集索引(所有六列只有 6 字节) 提供数据库(可用空间+恢复模型): (六列 x 1 字节 + 约 8 字节行开销)x 620mil = 数据库中约 9-10GB 的可用空间/存储空间,并最大限度地减少数据文件自动增长的开销。 如果可能,最好将 db 的恢复模式更改为 simple(以防止 log out of space 错误)。

如果空间使用很重要(并且有足够的 cpu 用于稍后增加的成本),您还可以考虑对“数字”表进行页面压缩。

创建数字表:

create table dbo.numstable
(
a tinyint not null,
b tinyint not null,
c tinyint not null,
d tinyint not null,
e tinyint not null,
f tinyint not null,
constraint pknumstable primary key clustered(a,b,c,d,e,f)
);

go

并填充它,一次一个列“a”值:

declare @maxa int = (select isnull(max(a), 0)+1 from dbo.numstable);
select @maxa = case when @maxa <= 85 then @maxa end;

declare @Numbers table (N tinyint primary key clustered);
insert into @Numbers(N) 
select rownum --cast?
from
(
select top (90) row_number() over(order by rand()) as rownum
from master.dbo.spt_values
) as r
where rownum >= @maxa;

insert into dbo.numstable with (tablock)
(a,b,c,d,e,f) 
SELECT N1.N as a, N2.N as b, N3.N as c, N4.N as d, N5.N as e, N6.N as f
FROM @Numbers N1
JOIN @Numbers N2 ON N2.N > N1.N
JOIN @Numbers N3 ON N3.N > N2.N
JOIN @Numbers N4 ON N4.N > N3.N
JOIN @Numbers N5 ON N5.N > N4.N
JOIN @Numbers N6 ON N6.N > N5.N
where N1.N = @maxa
order by N1.N, N2.N, N3.N, N4.N, N5.N, N6.N;
go 85 --iterate 85 times

如果在填充期间发生错误,请修复并再次执行填充脚本(原样)。

如果吞吐量约为 1500 万行/分钟(最坏情况),则完全填充表需要大约 40 分钟。

查询表时,您可以“利用”数据/值的性质:

--instead of ...
select count(*)
from dbo.numstable
where c = 7
and e = 44;

--...better...
select count(*)
from dbo.numstable
where a  in (1, 2, 3, 4, 5)
and b in (2, 3, 4, 5, 6)
and c = 7
and e = 44;

【讨论】:

以上是关于T-SQL:生成数字组合并保存到表中的主要内容,如果未能解决你的问题,请参考以下文章

将SP的输出游标合并到表中?

将 Firebase 数据合并到表视图中

无论如何将存储桶中的数据合并到表中但没有任何重复的行?

sql合并两张表(表字段不一致)到一张新表中

T-SQL 如何将存储结果的数据插入到表中

T-SQL:在 OUTPUT 子句中插入原始值