我知道我的问题可能没有“完美”的解决方案(这听起来像是背包或垃圾箱包装问题的变体),但这是我的情景:
我想将一个SQL数据库表的列表分成n个(比如说7个)大致相同大小的堆(因此我可以在整个一周内大致平均地传播一些维护任务)。
假设我有100个表(这可能更高或更低,但不可能高于5000),范围从1到10,000,000(当然,较大的表不常见)。
我最初的想法是按字母顺序(伪随机)对表进行排序,然后从头开始,当总数超过Sum(Size)/ 7时移动到下一个组。对于某些数据库来说,这可能会正常工作,但如果两个巨大的表彼此相邻,那么这就会造成非常不平等的群体。 (这听起来不太可能,考虑两个巨大的表,Account_History和Account_History_Archive)。
是否有任何普遍接受的技术可以为各种源数据提供“良好”的结果?我倾向于采用更简单的技术,而不是更精确的分组(如果维护在某些日子里运行的时间比其他人略长,那么 大部分交易)。
答案 0 :(得分:4)
如何按大小对表进行排序,然后对于每个表,将其放入当前具有最小总行数的那一天?这意味着最大的7个表格将首先传播。然后第8个最大的将与前7个中最小的一个,等等。你将继续以最少的工作量填写当天。
最终小参考表最终可能没什么区别。
你可以发明这不好的场景,但我希望它能在实践中发挥作用而不会太复杂。
答案 1 :(得分:1)
我不知道良好代码规模上的这个速率如何,但我要追求的解决方案是将作业列表放入优先级队列中,按成本最高的顺序排序,以及工作人员将垃圾箱分成另一个优先级队列,按照分配的最少工作排序,然后只从一个队列中弹出作业并将它们分配给顶部(最不忙)工作箱,直到没有工作。
答案 2 :(得分:1)
仅供参考,这就是我的处理方法。我想将“桶”放入持久化表中,并且每2周仅“重新计算”一次。否则,我担心如果每天计算这些存储桶,一张桌子可能会从一个存储桶跳到下一个存储桶。但是,我想经常重新计算一次以进行架构和DDL修改。这是片段。
-------------------------------------------------------------------------------------
--Get the total table size (by rows)
-------------------------------------------------------------------------------------
if object_id('tempdb..#Space') is not null
drop table #Space
SELECT
TableName = t.NAME,
Schem = s.name,
Pages = sum(a.total_pages),
Grp = row_number() over (order by sum(a.total_pages) desc)
INTO #Space
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN
sys.schemas s ON t.schema_id = s.schema_id
WHERE
t.NAME NOT LIKE 'dt%'
AND t.is_ms_shipped = 0
AND i.OBJECT_ID > 255
GROUP BY
t.Name, s.name
-------------------------------------------------------------------------------------
--split the tables into 7 buckets by:
--updating the Grp to the Grp with the lowest cumulative sum of all members by
--ordering by the current cumulative sum of all members
-------------------------------------------------------------------------------------
declare @ct int = 8
while @ct <= (select max(Grp) from #Space)
begin
update S
set Grp = (select top 1 Grp from #Space where Grp < 8 order by sum(Pages) over (partition by Grp) asc)
from #Space S
where S.Grp = @ct
set @ct = @ct + 1
end
insert into AdminTools..TableSpace (TableName
,Schem
,Pages
,Grp
,GrpPages
,LoadDate)
select
TableName
,Schem
,Pages
,Grp
,GrpPages = sum(Pages) over (partition by Grp)
,LoadDate = getdate()
from #Space
end