5

私の質問に対する「完璧な」解決策はおそらくないことを私は知っています(これはナップザックのバリエーションまたはビンパッキング問題のように聞こえます)が、これが私のシナリオです:

SQLデータベーステーブルのリストをn個(たとえば7個)のほぼ同じサイズのパイルに分割したいと思います(これにより、一部のメンテナンスタスクを1週間全体にほぼ均等に分散できます)。

サイズ1からサイズ10,000,000までの範囲の100個のテーブル(これは高くても低くてもかまいませんが、5000より高くなることはないでしょう)があるとしましょう(もちろん、大きなテーブルはそれほど一般的ではありません)。

私の当初のアイデアは、テーブルをアルファベット順に(疑似ランダムに)並べ替えてから、最初からウォークスルーし、合計がSum(Size)/7を超えると次のグループに移動することでした。一部のデータベースでは、これはおそらく正常に機能しますが、2つの巨大なテーブルが隣り合っている場合、これは非常に不均等なグループになります。(これは思ったほどありそうもないことではありません。Account_HistoryとAccount_History_Archiveの2つの巨大なテーブルを検討してください)。

さまざまなソースデータで「良好な」結果をもたらす、一般的に受け入れられている手法はありますか?私は、より正確なグループ化ではなく、より単純な手法に傾倒します(メンテナンスが他の日よりもわずかに長く実行される場合、それほど大きな問題ではありません)

4

3 に答える 3

5

テーブルをサイズで並べ替えてから、テーブルごとに、現在行の総数が最も少ない日に入れてはどうでしょうか。これは、最大の7つのテーブルが最初に日全体に広がることを意味します。次に、8番目に大きいものが最初の7つのうちの小さいものになります。たとえば、スケジュールされている作業量が最も少ない日を引き続き入力します。

小さな参照テーブルが最終的に終わる場所では、おそらく大きな違いはありません。

これが良くないシナリオを発明することもできますが、複雑になりすぎずに実際に機能することを期待しています。

于 2010-11-11T05:02:25.417 に答える
3

参考までに、これが私がこれについて行った方法です。「バケット」を永続化されたテーブルに入れて、2週間ごとにのみ「再計算」したかったのです。そうしないと、これらのバケットを毎日計算すると、テーブルが1つのバケットから次のバケットにジャンプする可能性があるのではないかと心配していました。しかし、スキーマと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
于 2018-10-09T13:03:23.013 に答える
1

これが適切なコードスケールでどのように評価されるかはわかりませんが、私が追求する解決策は、ジョブリストを最もコストのかかるもので並べ替えられた優先度キューに入れ、ワーカービンを最も少ない作業で並べ替えられた別の優先度付きキューに入れることです。割り当てられた後、1つのキューからジョブをポップして、作業がなくなるまで、それらを一番上の(最もビジーでない)ワーカービンに割り当てます。

于 2010-11-11T05:02:49.393 に答える