1. ホーム
  2. データベース
  3. エムエスエル

SQL SERVERのストアドプロシージャを使用した履歴データの移行について

2022-01-05 23:43:56

本日は、ソフトウェア開発において最も一般的な履歴データの移行方法についてお話します。移行について話す前に、いくつかの基本的な概念を簡単に紹介します。

1. 履歴データの移行とは?

簡単に言うと、作成から時間が経ち、あまり使われなくなった履歴データを別の場所(別のデータか別のテーブル)に保存することです。

2. 履歴データ移行の目的

使用するデータベースの数を減らす、データのボリュームが大きいほど、長いデータベースは、データを操作する(含む:クエリ、ソート、など)、データのテーブルが数千万レベルに達すると、その後、多条件のマルチテーブルクエリ、応答時間が遅くなる可能性があるためです。(開発者によって書かれたロジックが異なるため、すべてのSQLが効率的に実行されるSQLであることを保証することは不可能です)

ですから、タイムリーに過去のデータを移行していくことは、システム全体のパフォーマンスにとって確実なメリットとなります。

3. 履歴データの移行はいつ行う必要がありますか?

最も単純なケースでは、プログラムの動作が遅くなってきたと感じたときが、履歴データの移行を考え始めるタイミングとなります。

原則的には、サーバー台数が少なく、ハードウェア構成もそれほど高くない中小企業の場合、数千万件のデータが揃うのを待って移行を開始するのではなく、500万件以上のデータから1テーブルでゆっくりと移行を開始するのがよいでしょう。

1年程度は業務データを残したいということであれば、1年前の履歴データをすべて履歴データベースに移行すればよいでしょう。毎日発生するデータ量が多すぎる場合は、一般的にテーブルの自動分割保存を検討する必要がありますが、これを行わなければ、過去3~6カ月分のデータだけをライブビジネスデータベースに保存しておけば、日々の業務に影響を与えることはありません。

4. データ移行の基本的な考え方

1)、1回目の移行で同一のテーブル構造を作成する(1回目の移行前に作成したものであれば可)

2)、データを作成した時期でソートし、最も古いデータNデータをチェックアウトし、同時に履歴テーブルに挿入する。

insert into ... select from

3)、挿入されたデータの正確さをテストし、それがN正しいことを確認します。その後、使用中の業務データベースを削除してください。

4) 移行データの途中でエラーが発生した場合に処理を終了するが、使用中のデータベースを削除することはできないので、開発者にデータの確認をしてもらう必要がある。

5)、移行によるパフォーマンスへの影響にもよりますが、Nはあまり多くても一度に5Wから10Wのバー(サーバーのパフォーマンス構成によっては、一度に1Wから5Wのデータを移行する方が影響が少なくておすすめです)です。多くのデータを移行したい場合は、一括して実行することも検討できます。

5. データ移行用ストアドプロシージャのコード例

コードは以下の通りです。(あまり説明する必要はありません。非常にシンプルなコードで、一目で理解できます)

USE [Tyingsoft.GLPS]
GO
/****** Object: StoredProcedure [dbo]. [TY_SP_ApiRequestToHis] Script Date: 2021-09-16 15:35:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:
-- Create date:
-- Last Edit date:
-- Description: <Interface request log GLPS_APIREQUEST data migration>
-- =============================================
ALTER PROCEDURE [dbo]. [TY_SP_ApiRequestToHis]
    -- Add the parameters for the stored procedure here
    @PreCountN int = 2000 -- Number of entries per execution N
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    declare @tableDataCount int; -- Number of data items before migration
    declare @tableDataCountHis int; -- the number of data items in the historical database before migration
    declare @tableDataCount2 int; -- Number of data items after migration
    declare @tableDataCount2His int; -- the number of data items in the historical database after migration
    declare @maxCreateTime datetime; -- take the maximum creation time of the N data items
    declare @maxCreateTimeHis datetime; -- the maximum creation time in the historical database
    declare @beginTime datetime; -- start time
    declare @endTime datetime; --execution completion time
    declare @execTimeMS int; --execution time (in milliseconds)

    -- intermediate steps debugger time consuming use
    declare @tmpBeginTime datetime; --(temporary) start time of execution
    declare @tmpEndTime datetime; --(temporary) execution completion time
    declare @tmpExecTimeMS int; --(temporary) execution time (in milliseconds)

    select @beginTime = getdate();

    -- before migration: query the number of data entries first
    select @tableDataCount = count(1) from [Tyingsoft.GLPS].dbo.GLPS_APIREQUEST;
    select @tableDataCountHis =count(1) from [Tyingsoft.GLPS_His].dbo.GLPS_APIREQUEST;

    print '[Interface request record table (GLPS_APIREQUEST) data migration] start time:' + convert(nvarchar(50),@beginTime,20);
    print 'Number of data entries scheduled to be migrated this time: ' + cast( @PreCountN as nvarchar(20));

    --create a temporary common expression (the earliest N entries created in the table)
    with topNRecord (FCREATETIME)
    as
    (
        select top (@PreCountN) FCREATETIME from GLPS_APIREQUEST order by FCREATETIME
    )

    --get the maximum creation time in N data
    select @maxCreateTime =max(FCREATETIME) from topNRecord

    print 'The corresponding migration data FCREATETIME is:' + convert(nvarchar(50),@maxCreateTime,21); -- Convert the date to string format: yyyy-MM-dd HH:mm:ss.fff


    select @tmpBeginTime = GETDATE(); --Intermediate step to start timing

    -- Step 1: Write N count data to the history database
    insert into [Tyingsoft.GLPS_His].dbo.GLPS_APIREQUEST
    select * from [Tyingsoft.GLPS].dbo.GLPS_APIREQUEST where FCREATETIME <=@maxCreateTime

    select @tmpEndTime = GETDATE(); -- intermediate step timed out
    print 'Data migration, insert elapsed time (milliseconds):' +cast( datediff(millisecond,@tmpBeginTime,@tmpEndTime) as nvarchar(20));


    -- Step 2: Compare data from historical database
    select @maxCreateTimeHis=max(FCREATETIME) from [Tyingsoft.GLPS_His].dbo.GLPS_APIREQUEST

    if @maxCreateTime = @maxCreateTimeHis
       begin
            select @tmpBeginTime = GETDATE(); --Intermediate step starts timing

            -- Step 3: After execution, delete the data
            delete from GLPS_APIREQUEST where FCREATETIME <=@maxCreateTime
            print 'Number of data entries deleted after migration:' + cast( @@ROWCOUNT as nvarchar(50));

            select @tmpEndTime = GETDATE(); -- intermediate step timed out
            print 'Data migration, deletion elapsed time (milliseconds):' + cast( datediff(millisecond,@tmpBeginTime,@tmpEndTime) as nvarchar(20));
        end
    else
        print 'After migration, date checksum error, data not deleted!!!'

    -- After migration: query data count again
    select @tableDataCount2 = count(1) from [Tyingsoft.GLPS].dbo.GLPS_APIREQUEST;
    select @tableDataCount2His =count(1) from [Tyingsoft.GLPS_His].dbo.GLPS_APIREQUEST;

    print 'Number of data items in GLPS_APIREQUEST before migration:' + cast(@tableDataCount as nvarchar(20))
     + ',Number of data entries after migration:' + cast(@tableDataCount2 as nvarchar(20))
     + ',Difference: ' + cast((@tableDataCount2-@tableDataCount) as nvarchar(20));

    print 'Number of data entries in His.GLPS_APIREQUEST before migration:'
    + cast(@tableDataCountHis as nvarchar(20))
    + ',Number of data entries after migration:' + cast(@tableDataCount2His as nvarchar(20))
    + ',Difference: '+cast((@tableDataCount2His-@tableDataCountHis) as nvarchar(20));

    print 'Note: The formal environment has always had data changes, so there will be some deviation!' ;


    select @endTime = GETDATE();
    print 'Total elapsed time (milliseconds):' +cast( datediff(millisecond,@beginTime,@endTime) as nvarchar(20));


END

では、テスト用のデータベースで試してみましょう。

6. 使用シナリオに関する特記事項

この方法は、insert into ... select from を使ってデータを移行する方法です。この考え方は、最もシンプルなデータ移行ロジックですが、データ量が少ない場合(一般的に500万件未満のテーブルデータ)、データ量が500万件を超える場合は、この方法を使用しないでください。

また、この方法はデータベースにSQLで直接アクセスするため、現在のビジネスライブラリと過去のデータの両方にアクセスできること(つまり同じデータベースインスタンス)が必要で、オフサイトの場合は別のデータベースを扱う方法がない、という事実もあります。

そのため、この方法は単純な履歴データの移行シナリオにのみ適しており、使用前提条件が限定されているため、小規模なプロジェクトに向いています。

大容量のデータ(1つのテーブルで1000万以上)や、現在の業績指標に影響を与えないデータ移行方法については、次の記事で詳しく解説しています。

SQL SERVERのストアドプロシージャのデータ移行については、スクリプトハウスの過去記事を検索していただくか、引き続き以下の関連記事をご覧ください。