web-dev-qa-db-ja.com

既存のパーティションから新しいパーティションにデータを作成/移動できないSQL Server 2014

SQL Server 2014 Enterprise Editionと約6 TB=のサイズのデータ​​ベースがあります。

サーバーがAzureにあり、9のプレミアムディスクがあるため、インフラの素早いバックグラウンドを提供するためだけにTB for datafiles。

識別子(MonthlyDate integer Eg 01012016、... 31122016)によって既にパーティション分割されているいくつかの大きなテーブルを用意します。 2014年から2016年までの月次パーティションがあります(01012014、01022014 ...... 31122016まで)。

今、私たちは2017年と2018年のパーティションを月単位で作成しようとしています。

パーティションウィザードを使用しようとしましたが、必要なオプションが見つかりませんでした。私が知る限り、私は次のようなものを実行する必要があります:

Alter partition Scheme [PartPScheme_BIGTAB] Next Used [PartFileGrp_201701]

Alter partition Function [PartPFN_BIGTAB] () split range(20170131)

上記のスクリプトを複数回実行しようとしましたが、7時間以上かかって、データの1 GBのみがパーティションの上に移動されたので、最後にスクリプトをロールバックする必要があります。

この種のベストプラクティスについて、正しい方向に向けてくれる人を探しています。これにどのように取り組むか本当にわかりません。

問題は何でしょうか?

現在のパーティション関数は次のとおりです。

CREATE PARTITION FUNCTION [PartPFN_BIGTAB](int) AS RANGE LEFT FOR VALUES (
      20141031
    , 20141131
    , 20141231
    , 20150131
    , 20150231
    , 20150331
    , 20150431
    , 20150531
    , 20150631
    , 20150731
    , 20150831
    , 20150931
    , 20151031
    , 20151131
    , 20151231
    , 20160131
    , 20160231
    , 20160331
    , 20160431
    , 20160531
    , 20160631
    , 20160731
    , 20160831
    , 20160931
    , 20161031
    , 20161131
    , 20161231
    );
4
AzimL

あなたの意図がこのデータを年/月ごとに分割することである場合、私はあなたの現在の分割列(整数であり、「真の」日付データ型ではなく、整数としてもyyyymmdd形式でさえない)はすべてジョブにとって間違っていると思います。また、表にIS真の日付データ型のMonthlyDateである別の列がないと仮定します。

私の意見では(さらにディスク容量がある場合)、次のことを行う必要があります。

  • DATEデータ型を使用する新しいパーティション関数を作成します。これはRANGE RIGHT(説明については、この回答の下部にあるDan Guzmanのリンクを参照してください)。最小日付よりかなり下の範囲と、最大日付よりかなり上の範囲で作成してください。また、時間がたつにつれて、空のパーティションを、すでにデータが入力されているデータよりもはるかに先に分割していることを確認してください。
  • 新しいパーティション関数を参照する新しいパーティション構成を作成します。
  • 現在のテーブルの作成をスクリプト化して、新しい「置換」テーブル(新しく作成されたパーティション構成を参照する)を作成しますが、整数のMonthlyDate列から派生/変換された計算/永続DATE列(ex:PartitionedColumn DATE)を追加します。
  • トランザクション内で、挿入ステートメントをセットアップし、既存のテーブルから新しいテーブルにレコードを挿入します。たとえば、バッチで数千または10万回です。トランザクションが完了したら、ログのバックアップを実行します(単純な回復でない場合)。これにより、トランザクションログの領域が不足しなくなります。次に、バッチを繰り返します...すべてのレコードが永続化された計算列を持つ新しいテーブルに書き込まれるまで、それを繰り返します。
  • 次に、古いテーブル名を変更し、新しいテーブル名を古いテーブルの名前に変更します

パーティション化シナリオに参加するすべての一意のインデックスは、単に含まれている列ではなく、ベースインデックス定義の一部として定義されているパーティション化列を持つ必要があることに注意してください。

また、パーティション切り替え(「スライディングウィンドウ」でよく使用される)を有効にするには、テーブルのすべてのインデックスを揃える必要があります。 BOLはこれについて良い情報を持っています。 パーティション化インデックスの特別なガイドライン を探します。

最後に、Dan GuzmanのSQL Serverパーティショニングに関する excellent post を確認してください。

3
Scott Hodgin

年が始まる前に、2017パーティションを作成しておく必要があります。これで、最後のパーティションには2016年12月1日から現在までのすべてのデータが含まれています。分割するときは、すべての行を物理的に新しいパーティションに移動する必要があります。

参照: https://docs.Microsoft.com/en-us/sql/t-sql/statements/alter-partition-function-transact-sql

パーティションの分割(新しいデータをロードする前)とパーティションのマージ(古いデータをアンロードした後)でデータの移動が発生しないように、常にパーティション範囲の両端に空のパーティションを保持します。移入されたパーティションを分割またはマージしないでください。これにより、ログ生成が4倍も多くなり、重大なロックが発生する可能性があるため、これは非常に非効率的です。

IOへの影響を最小限に抑えるために使用できるアプローチはいくつかあります。以下の記事に記載されている解決策は最高の解決策の1つです。また、Microsoftの最高のフィールドエンジニアチームによって書かれています。

おっと…空のSQLテーブルパーティションを残すのを忘れたのですが、最小限の分割でどうすれば分割できますかIO影響?

ケンドラリトルによるもう1つの良い読み物です。

左ベースのパーティション関数の下端にパーティションを追加

編集:@ScottHodginのコメントに基づくと、パーティション列が整数データ型であり、日時型または日付型ではない場合、より大きな問題が発生する可能性があります。同時にこれを修正したいかもしれません。

2
SqlWorldWide

コメントの追加情報に基づいて、現在のパーティション関数の境界値はYYYYMM31のようです。実際のパーティション列の値がYYYYMMDD形式であると想定すると、一時的なパーティション関数とスキームと共にステージングテーブルとSWITCHを使用できるはずです。

以下は、このメソッドを使用したスクリプトの例です。空ではないパーティションの分割を避けるために、事前に計画するのが最善であるとScottとSqlWordWideに同意します。変更できないレガシースキーマがない限り、パーティション列は日付データ型である必要があります。これにより、有効な日付のみが存在し、より直感的なパーティション境界とメンテナンススクリプトが可能になります。

--create temporary partition function and scheme with desired boundaries and filegroup mapping
DECLARE
      @StartMonth date = '20141001'
    , @EndMonth date = '20181201'
    , @Month date
    , @SQL nvarchar(MAX);

CREATE PARTITION FUNCTION PartPFN_BIGTAB_Temp(int) AS RANGE LEFT FOR VALUES ();
CREATE PARTITION SCHEME PartPScheme_BIGTAB_Temp
    AS PARTITION PartPFN_BIGTAB_Temp ALL TO ([DEFAULT]);
SET @Month = @StartMonth;
WHILE @Month <= @EndMonth
BEGIN
    SET @SQL = REPLACE(
          N'ALTER PARTITION SCHEME PartPScheme_BIGTAB_Temp NEXT USED PartFileGrp_$(YYYYMM);'
        , N'$(YYYYMM)'
        , LEFT(CONVERT(char(8), @Month, 112), 6)
        );
    PRINT @SQL;
    EXEC sp_executesql @SQL;
    ALTER PARTITION FUNCTION PartPFN_BIGTAB_Temp()
        SPLIT RANGE(CAST(LEFT(CONVERT(char(8), @Month, 112), 6) + '31' AS int));
    SET @Month = DATEADD(month, 1, @Month);
END;
GO

--create aligned temporary staging table with same schema and indexes on original partition scheme
CREATE TABLE dbo.YourPartitionedTable_Staging(
      YYYYMMDDColumn int
    , OtherColumn int
    , CONSTRAINT PK_YourPartitionedTable_Staging
      PRIMARY KEY (
        OtherColumn
        , YYYYMMDDColumn
        ) ON PartPScheme_BIGTAB(YYYYMMDDColumn)
) ON PartPScheme_BIGTAB(YYYYMMDDColumn);

--switch last partition ( first value > 20161231 ) into staging table
ALTER TABLE dbo.YourPartitionedTable SWITCH
    PARTITION $PARTITION.PartPFN_BIGTAB(20161232)
    TO dbo.YourPartitionedTable_Staging
    PARTITION $PARTITION.PartPFN_BIGTAB(20161232);

--recreate each index on temporary scheme using CREATE...INDEX...DROP_EXISTING
CREATE UNIQUE CLUSTERED INDEX PK_YourPartitionedTable_Staging ON dbo.YourPartitionedTable_Staging
    (
          OtherColumn
        , YYYYMMDDColumn
    )
    WITH (DROP_EXISTING=ON)
    ON PartPScheme_BIGTAB_Temp(YYYYMMDDColumn);
GO

--create new boundaries in original partition function along with filegroups mappings
DECLARE
      @StartMonth date = '20170101'
    , @EndMonth date = '20181201'
    , @Month date
    , @SQL nvarchar(MAX);

SET @Month = @StartMonth;
WHILE @Month <= @EndMonth
BEGIN
    SET @SQL = REPLACE(
          N'ALTER PARTITION SCHEME PartPScheme_BIGTAB NEXT USED PartFileGrp_$(YYYYMM);'
        , N'$(YYYYMM)'
        , LEFT(CONVERT(char(8), @Month, 112), 6)
        );
    PRINT @SQL;
    EXEC sp_executesql @SQL;
    ALTER PARTITION FUNCTION PartPFN_BIGTAB()
        SPLIT RANGE(CAST(LEFT(CONVERT(char(8), @Month, 112), 6) + '31' AS int));
    SET @Month = DATEADD(month, 1, @Month);
END;
GO

--SWITCH data in new parttions bach into original table
DECLARE
      @StartMonth date = '20171001'
    , @EndMonth date = '20181201'
    , @Month date;

SET @Month = @StartMonth;
WHILE @Month <= @EndMonth
BEGIN

    ALTER TABLE dbo.YourPartitionedTable_Staging SWITCH
        PARTITION $PARTITION.PartPFN_BIGTAB(CAST(LEFT(CONVERT(char(8), @Month, 112), 6) + '31' AS int))
        TO dbo.YourPartitionedTable
        PARTITION $PARTITION.PartPFN_BIGTAB(CAST(LEFT(CONVERT(char(8), @Month, 112), 6) + '31' AS int));

    SET @Month = DATEADD(month, 1, @Month);
END;
GO

--generally a good practice to update stats after SWITCH, although may not be reqiired here because data did not change
UPDATE STATISTICS dbo.YourPartitionedTable;
GO

--remove temporary objects
DROP TABLE dbo.YourPartitionedTable_Staging
DROP PARTITION SCHEME PartPScheme_BIGTAB_Temp;
DROP PARTITION FUNCTION PartPFN_BIGTAB_Temp;
GO
0
Dan Guzman