Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Đồng bộ hóa cấu trúc cơ sở dữ liệu giữa các ứng dụng

Bất kỳ ai đã từng phát triển ứng dụng sử dụng cơ sở dữ liệu đều có thể gặp phải vấn đề cập nhật cấu trúc cơ sở dữ liệu khi ứng dụng được triển khai và cập nhật.

Cách tiếp cận phổ biến nhất là tạo một bộ tập lệnh SQL để sửa đổi cấu trúc cơ sở dữ liệu từ phiên bản này sang phiên bản khác. Tất nhiên, có những công cụ trả phí, nhưng không phải lúc nào chúng cũng giải quyết được vấn đề tự động hóa hoàn toàn bản cập nhật.

Công nghệ di chuyển, được giới thiệu lần đầu trong Hibernate ORM và được triển khai trong Linq, rất tốt và thuận tiện, nhưng nó ngụ ý chiến lược "viết mã trước" để phát triển cấu trúc cơ sở dữ liệu, vốn rất tốn công cho các dự án hiện có và việc sử dụng các trình kích hoạt, các thủ tục được lưu trữ và các chức năng trong cơ sở dữ liệu khiến cho việc chuyển đổi sang chiến lược “viết mã trước” hầu như không thể.

Bài viết này đề xuất một cách tiếp cận thay thế để giải quyết vấn đề này - lưu trữ cấu trúc cơ sở dữ liệu tham chiếu trong tệp XML và tự động tạo tập lệnh SQL dựa trên sự so sánh của tham chiếu và cấu trúc hiện có. Vì vậy, hãy bắt đầu…

Tạo tệp XML với cấu trúc cơ sở dữ liệu

Chúng tôi sẽ sử dụng cơ sở dữ liệu DbSyncSample. Tập lệnh để tạo cơ sở dữ liệu được hiển thị bên dưới.

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

Tạo một ứng dụng bảng điều khiển và liên kết gói nuget Shed.DbSync với nó.

Cấu trúc cơ sở dữ liệu XML như sau:

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);
        }
    }

Sau khi chạy chương trình, chúng tôi thấy thông tin sau trong tệp 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>

Triển khai / cập nhật cấu trúc cơ sở dữ liệu bằng XML

Tạo cơ sở dữ liệu DbSyncSampleCopy trống khác, thêm mã sau vào mã chương trình bảng điều khiển:

 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
        }
    }

Sau khi chạy chương trình, bạn có thể xác minh rằng DbSyncSampleCopy hiện có cấu trúc bảng giống hệt với cơ sở dữ liệu tham chiếu. Vui lòng thử nghiệm với việc thay đổi cấu trúc tham chiếu và cập nhật cấu trúc mục tiêu.

Trong các tình huống thử nghiệm, bạn có thể cần tạo cơ sở dữ liệu thử nghiệm mọi lúc mọi nơi. Trong trường hợp này, sẽ hữu ích khi sử dụng hàm Shed.DbSync.DataBase.ClearDb (string connString).

Theo dõi cấu trúc cơ sở dữ liệu tự động

Chức năng theo dõi cấu trúc được tạo thành một chức năng riêng biệt, chức năng này sẽ được gọi khi bắt đầu / khởi động lại ứng dụng hoặc ở một nơi khác theo yêu cầu của nhà phát triển.

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

Theo dõi được thực hiện bằng cách sử dụng tham số Phiên bản (thẻ) trong XML. Tình huống sử dụng quy trình như sau:

  1. Gán một phiên bản cho cơ sở dữ liệu. Trong Microsoft SQL Server Management Studio, bấm chuột phải vào nút của cơ sở dữ liệu được yêu cầu và chọn Thuộc tính.

  2. Tiếp theo, nhấp vào Thuộc tính Mở rộng và thêm thuộc tính Phiên bản có giá trị 1 vào bảng thuộc tính. Với mỗi lần sửa đổi cấu trúc tiếp theo, thuộc tính này sẽ được tăng thêm 1.

  3. Khi bạn khởi động ứng dụng, tệp sẽ được tạo, nếu không có tệp XML hoặc phiên bản của nó nhỏ hơn phiên bản của cơ sở dữ liệu.

  4. Nếu phiên bản của tệp XML lớn hơn phiên bản của cơ sở dữ liệu, thì một tập lệnh để cập nhật cơ sở dữ liệu sẽ được tạo và thực thi.

  5. Nếu xảy ra lỗi trong quá trình thực thi tập lệnh, tất cả các thay đổi sẽ được khôi phục.

  6. Kết quả đồng bộ hóa được ghi vào tệp nhật ký được tạo trong thư mục được chỉ định bởi tham số logDitPath.

  7. Nếu tham số SqlScriptPath được chỉ định, một tệp có tập lệnh từ mục 4 sẽ được tạo.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách theo dõi tình trạng của các phiên bản cơ sở dữ liệu

  2. Các hàm cửa sổ lồng nhau trong SQL

  3. Những điều thú vị về INSTEAD OF trigger

  4. Bắt đầu với Kênh Django

  5. Nhiều khóa chính có thể tồn tại trên một bảng không?