sql >> データベース >  >> RDS >> Database

アプリケーション間のデータベース構造の同期

    データベースを使用するアプリケーションを開発したことがある人は、アプリケーションのデプロイと更新時にデータベース構造を更新するという問題に直面した可能性があります。

    最も一般的なアプローチは、SQLスクリプトのセットを作成して、データベース構造をバージョンごとに変更することです。もちろん、有料のツールもありますが、アップデートの完全自動化の問題を常に解決できるとは限りません。

    Hibernate ORMで最初に導入され、Linqで実装された移行テクノロジは非常に優れており便利ですが、データベース構造を開発するための「コードファースト」戦略を意味します。これは既存のプロジェクトにとって非常に面倒であり、データベースでトリガー、ストアドプロシージャ、および関数を使用すると、「コードファースト」戦略への移行がほぼ不可能になります。

    この記事では、この問題を解決するための代替アプローチを提案します。参照データベース構造をXMLファイルに保存し、参照との比較に基づいてSQLスクリプトを自動的に生成します。既存の構造。では、始めましょう…

    データベース構造を使用したXMLファイルの生成

    DbSyncSampleデータベースを使用します。データベースを作成するためのスクリプトを以下に示します。

    USE [DbSyncSample]
    GO
    /****** Object:  Table [dbo].[Orders]    Script Date: 06/01/2017 10:37:43 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[Orders](
    	[Id] [int] IDENTITY(1,1) NOT NULL,
    	[OrderNumber] [nvarchar](50) NULL,
    	[OrderTime] [datetime] NULL,
    	[TotalCost] [decimal](18, 2) NOT NULL,
     CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
    (
    	[Id] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    CREATE NONCLUSTERED INDEX [IX_Orders_OrderNumber] ON [dbo].[Orders] 
    (
    	[OrderNumber] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    GO
    /****** Object:  Table [dbo].[Details]    Script Date: 06/01/2017 10:37:43 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[Details](
    	[Id] [int] IDENTITY(1,1) NOT NULL,
    	[Descript] [nvarchar](150) NULL,
    	[OrderId] [int] NULL,
    	[Cost] [decimal](18, 2) NOT NULL,
     CONSTRAINT [PK_Details] PRIMARY KEY CLUSTERED 
    (
    	[Id] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    /****** Object:  Trigger [Details_Modify]    Script Date: 06/01/2017 10:37:43 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TRIGGER [dbo].[Details_Modify]
       ON  [dbo].[Details] 
       AFTER INSERT,UPDATE
    AS 
    BEGIN
    	UPDATE Orders
    	SET TotalCost = s.Total
    	FROM (
    		SELECT i.OrderId OId, SUM(d.Cost) Total
    		FROM Details d
    		JOIN inserted i ON d.OrderId=i.OrderId
    		GROUP BY i.OrderId
    	) s
    	WHERE Id=s.OId
    END
    GO
    /****** Object:  Trigger [Details_Delete]    Script Date: 06/01/2017 10:37:43 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TRIGGER [dbo].[Details_Delete]
       ON  [dbo].[Details] 
       AFTER DELETE
    AS 
    BEGIN
    	UPDATE Orders
    	SET TotalCost = s.Total
    	FROM (
    		SELECT i.OrderId OId, SUM(d.Cost) Total
    		FROM Details d
    		JOIN deleted i ON d.OrderId=i.OrderId
    		GROUP BY i.OrderId
    	) s
    	WHERE Id=s.OId
    END
    GO
    /****** Object:  Default [DF_Details_Cost]    Script Date: 06/01/2017 10:37:43 ******/
    ALTER TABLE [dbo].[Details] ADD  CONSTRAINT [DF_Details_Cost]  DEFAULT ((0)) FOR [Cost]
    GO
    /****** Object:  Default [DF_Orders_TotalCost]    Script Date: 06/01/2017 10:37:43 ******/
    ALTER TABLE [dbo].[Orders] ADD  CONSTRAINT [DF_Orders_TotalCost]  DEFAULT ((0)) FOR [TotalCost]
    GO
    /****** Object:  ForeignKey [FK_Details_Orders]    Script Date: 06/01/2017 10:37:43 ******/
    ALTER TABLE [dbo].[Details]  WITH CHECK ADD  CONSTRAINT [FK_Details_Orders] FOREIGN KEY([OrderId])
    REFERENCES [dbo].[Orders] ([Id])
    GO
    ALTER TABLE [dbo].[Details] CHECK CONSTRAINT [FK_Details_Orders]
    GO

    コンソールアプリケーションを作成し、Shed.DbSyncnuget-packageをそれにリンクします。

    XMLデータベースの構造は次のとおりです。

    class Program
        {
            private const string OrigConnString = "data source=.;initial catalog=FiocoKb;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
            static void Main(string[] args)
            {
                // getting XML with the database structure
                var db = new Shed.DbSync.DataBase(OrigConnString);
                var xml = db.GetXml();
                File.WriteAllText("DbStructure.xml", xml);
            }
        }

    プログラムを実行すると、DbStructure.xmlファイルに次のように表示されます。

    <?xml version="1.0"?>
    <DataBase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Version>0</Version>
      <Tables>
        <Table Name="Orders" ObjectId="2137058649" ParentObjectId="0">
          <Columns>
            <Column Name="Id">
              <ColumnId>1</ColumnId>
              <Type>int</Type>
              <MaxLength>4</MaxLength>
              <IsNullable>false</IsNullable>
              <IsIdentity>true</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
            <Column Name="OrderNumber">
              <ColumnId>2</ColumnId>
              <Type>nvarchar</Type>
              <MaxLength>100</MaxLength>
              <IsNullable>true</IsNullable>
              <IsIdentity>false</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
            <Column Name="OrderTime">
              <ColumnId>3</ColumnId>
              <Type>datetime</Type>
              <MaxLength>8</MaxLength>
              <IsNullable>true</IsNullable>
              <IsIdentity>false</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
            <Column Name="TotalCost">
              <ColumnId>4</ColumnId>
              <Type>decimal</Type>
              <MaxLength>9</MaxLength>
              <IsNullable>false</IsNullable>
              <IsIdentity>false</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
          </Columns>
          <Indexes>
            <Index Name="PK_Orders">
              <IndexId>1</IndexId>
              <Type>CLUSTERED</Type>
              <IsUnique>true</IsUnique>
              <IsPrimaryKey>true</IsPrimaryKey>
              <IsUniqueConstraint>false</IsUniqueConstraint>
              <Columns>
                <IndexColumn>
                  <TableColumnId>1</TableColumnId>
                  <KeyOrdinal>1</KeyOrdinal>
                  <IsDescendingKey>false</IsDescendingKey>
                </IndexColumn>
              </Columns>
            </Index>
            <Index Name="IX_Orders_OrderNumber">
              <IndexId>2</IndexId>
              <Type>NONCLUSTERED</Type>
              <IsUnique>false</IsUnique>
              <IsPrimaryKey>false</IsPrimaryKey>
              <IsUniqueConstraint>false</IsUniqueConstraint>
              <Columns>
                <IndexColumn>
                  <TableColumnId>2</TableColumnId>
                  <KeyOrdinal>1</KeyOrdinal>
                  <IsDescendingKey>false</IsDescendingKey>
                </IndexColumn>
              </Columns>
            </Index>
          </Indexes>
          <PrimaryKey Name="PK_Orders" ObjectId="5575058" ParentObjectId="2137058649">
            <UniqueIndexId>1</UniqueIndexId>
          </PrimaryKey>
          <ForeignKeys />
          <Defaults>
            <Default Name="DF_Orders_TotalCost" ObjectId="69575286" ParentObjectId="2137058649">
              <ParentColumnId>4</ParentColumnId>
              <Definition>((0))</Definition>
            </Default>
          </Defaults>
        </Table>
        <Table Name="Details" ObjectId="85575343" ParentObjectId="0">
          <Columns>
            <Column Name="Id">
              <ColumnId>1</ColumnId>
              <Type>int</Type>
              <MaxLength>4</MaxLength>
              <IsNullable>false</IsNullable>
              <IsIdentity>true</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
            <Column Name="Descript">
              <ColumnId>2</ColumnId>
              <Type>nvarchar</Type>
              <MaxLength>300</MaxLength>
              <IsNullable>true</IsNullable>
              <IsIdentity>false</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
            <Column Name="OrderId">
              <ColumnId>3</ColumnId>
              <Type>int</Type>
              <MaxLength>4</MaxLength>
              <IsNullable>true</IsNullable>
              <IsIdentity>false</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
            <Column Name="Cost">
              <ColumnId>4</ColumnId>
              <Type>decimal</Type>
              <MaxLength>9</MaxLength>
              <IsNullable>false</IsNullable>
              <IsIdentity>false</IsIdentity>
              <IsComputed>false</IsComputed>
            </Column>
          </Columns>
          <Indexes>
            <Index Name="PK_Details">
              <IndexId>1</IndexId>
              <Type>CLUSTERED</Type>
              <IsUnique>true</IsUnique>
              <IsPrimaryKey>true</IsPrimaryKey>
              <IsUniqueConstraint>false</IsUniqueConstraint>
              <Columns>
                <IndexColumn>
                  <TableColumnId>1</TableColumnId>
                  <KeyOrdinal>1</KeyOrdinal>
                  <IsDescendingKey>false</IsDescendingKey>
                </IndexColumn>
              </Columns>
            </Index>
          </Indexes>
          <PrimaryKey Name="PK_Details" ObjectId="117575457" ParentObjectId="85575343">
            <UniqueIndexId>1</UniqueIndexId>
          </PrimaryKey>
          <ForeignKeys>
            <ForeignKey Name="FK_Details_Orders" ObjectId="149575571" ParentObjectId="85575343">
              <ReferenceTableId>2137058649</ReferenceTableId>
              <References>
                <Reference>
                  <ColumnId>1</ColumnId>
                  <ParentColumnId>3</ParentColumnId>
                  <ReferenceColumnId>1</ReferenceColumnId>
                </Reference>
              </References>
              <DeleteAction>NO_ACTION</DeleteAction>
              <UpdateAction>NO_ACTION</UpdateAction>
            </ForeignKey>
          </ForeignKeys>
          <Defaults>
            <Default Name="DF_Details_Cost" ObjectId="101575400" ParentObjectId="85575343">
              <ParentColumnId>4</ParentColumnId>
              <Definition>((0))</Definition>
            </Default>
          </Defaults>
        </Table>
      </Tables>
      <Views />
      <ProgrammedObjects>
        <ProgObject Name="Details_Modify" ObjectId="165575628" ParentObjectId="0">
          <Definition>CREATE TRIGGER [dbo].[Details_Modify]
       ON  dbo.Details 
       AFTER INSERT,UPDATE
    AS 
    BEGIN
    	UPDATE Orders
    	SET TotalCost = s.Total
    	FROM (
    		SELECT i.OrderId OId, SUM(d.Cost) Total
    		FROM Details d
    		JOIN inserted i ON d.OrderId=i.OrderId
    		GROUP BY i.OrderId
    	) s
    	WHERE Id=s.OId
    END</Definition>
          <Type>SQL_TRIGGER</Type>
        </ProgObject>
        <ProgObject Name="Details_Delete" ObjectId="181575685" ParentObjectId="0">
          <Definition>CREATE TRIGGER [dbo].[Details_Delete]
       ON  dbo.Details 
       AFTER DELETE
    AS 
    BEGIN
    	UPDATE Orders
    	SET TotalCost = s.Total
    	FROM (
    		SELECT i.OrderId OId, SUM(d.Cost) Total
    		FROM Details d
    		JOIN deleted i ON d.OrderId=i.OrderId
    		GROUP BY i.OrderId
    	) s
    	WHERE Id=s.OId
    END</Definition>
          <Type>SQL_TRIGGER</Type>
        </ProgObject>
      </ProgrammedObjects>
    </DataBase>

    XMLを使用したデータベース構造の展開/更新

    別の空のDbSyncSampleCopyデータベースを作成し、コンソールプログラムコードに次のコードを追加します。

     class Program
        {
            private const string OrigConnString = "data source=.;initial catalog=DbSyncSample;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
            private const string TargetConnString = "data source=.;initial catalog=DbSyncSampleCopy;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
    
            static void Main(string[] args)
            {
                //  getting XML with the structure of the reference database
                var dborig = new Shed.DbSync.DataBase(OrigConnString);
                var xml = dborig.GetXml();
                File.WriteAllText("DbStructure.xml", xml);
    
                //  if you need to clear the structure of the target database, use
                //  Shed.DbSync.DataBase.ClearDb(TargetConnString);
    
                //  update the structure of the target database
                var dbcopy = Shed.DbSync.DataBase.CreateFromXml(xml);
                dbcopy.UpdateDb(TargetConnString);
                //  in fact, you can use one line:
                //  dborig.UpdateDb(TargetConnString);
                //  create dbcopy only to demonstrate the creation of a database object from XML
            }
        }

    プログラムを実行した後、DbSyncSampleCopyが参照データベースと同じテーブル構造になっていることを確認できます。参照構造を変更し、ターゲット構造を更新してみてください。

    テストシナリオでは、毎回最初からテストデータベースを作成する必要がある場合があります。この場合、Shed.DbSync.DataBase.ClearDb(string connString)関数を使用すると便利です。

    データベース構造の自動追跡

    構造追跡は別の関数になります。この関数は、アプリケーションの開始/再起動時、または開発者の要求に応じて別の場所で呼び出す必要があります。

    static void SyncDb()
            {
                // autotracking of database structure
                Shed.DbSync.DataBase.Syncronize(OrigConnString, 
                    @"Struct\DbStructure.xml",      //  path to the structure file
                    @"Struct\Logs",                 //  path to synchronization log folder
                    @"Struct\update_script.sql"     //  (optional) in case of defining this parameter
                                                    //  the script generated for the database update  
                                                    //  will be stored within it
                );
            }SCRIPT

    追跡は、XMLのバージョンパラメータ(タグ)を使用して実行されます。手順を使用するシナリオは次のとおりです。

    1. データベースにバージョンを割り当てます。 Microsoft SQL Server Management Studioで、必要なデータベースのノードを右クリックし、[プロパティ]を選択します。

    2. 次に、[拡張プロパティ]をクリックし、値1のVersionプロパティをプロパティテーブルに追加します。その後、構造を変更するたびに、このプロパティを1ずつ増やす必要があります。

    3. アプリケーションを起動すると、XMLファイルがないか、そのバージョンがデータベースのバージョンよりも小さい場合、ファイルが作成されます。

      >
    4. XMLファイルのバージョンがデータベースのバージョンよりも大きい場合、データベースを更新するためのスクリプトが生成され、実行されます。

    5. スクリプトの実行中にエラーが発生した場合、すべての変更がロールバックされます。

    6. 同期結果は、logDitPathパラメーターで指定されたフォルダーに作成されたログファイルに書き込まれます。

    7. SqlScriptPathパラメーターが指定されている場合、項目4のスクリプトを含むファイルが作成されます。


    1. SQLServerでの照合

    2. MySQL、Postgres、Aurora用のサーバーレスGraphQLAPIを作成する方法

    3. 別のテーブルに存在しない1つのテーブルからすべてのレコードを選択するにはどうすればよいですか?

    4. mysqlデータベースで出現するすべての文字列を検索します