التحكم في الإصدار داخل SQL Server

جوليا : إذن ، من غيّر عملي يوم أمس؟
ليشا : ليس أنا
مكسيم : ليس أنا
- شباب ، هل يمكننا الحصول على جيت؟
Seryozha : لقد حان الوقت!
لقد مرت أسبوعين ...

جوليا : يا رفاق؟
- نعم ، ألم ترتكب؟
جوليا : لا لعنة (...

هكذا بدأ كل شيء. حسنا ، ماذا ، كل حرف وكل سطر يرتكب؟

أو ربما سيحدث كل هذا بمفرده؟) عند هذه النقطة ، يبدأون في التفكير
يتم تشغيل مشغلات DDL والجدول الزمني والصورة. حلها ، سنقوم بتخزين الإصدارات في الداخل
SQL Server'a !)




أولاً ، قم بإنشاء الجداول التي سيتم تخزين الإصدارات فيها

USE master GO --     IF NOT EXISTS ( SELECT 1 FROM sys.objects WHERE name = 'VersionControlHistory' AND type = 'U' ) CREATE TABLE dbo.VersionControlHistory( Id INT NOT NULL, Event sysname NOT NULL, Db sysname NOT NULL, Sch sysname NOT NULL, Object sysname NOT NULL, Sql XML NOT NULL, Login sysname NOT NULL, StartDate DATETIME2(0) NOT NULL, EndDate DATETIME2(0) NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO --      IF NOT EXISTS ( SELECT 1 FROM sys.objects WHERE name = 'VersionControl' AND type = 'U' ) CREATE TABLE dbo.VersionControl( Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_VersionControl PRIMARY KEY NONCLUSTERED, Event sysname NOT NULL, Db sysname NOT NULL, Sch sysname NOT NULL, Object sysname NOT NULL, Sql XML NOT NULL, Login sysname NOT NULL, StartDate DATETIME2(0) GENERATED ALWAYS AS ROW START NOT NULL, EndDate DATETIME2(0) GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME (StartDate, EndDate) ) WITH ( SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.VersionControlHistory ) ) GO 

من المهم أن نأخذ في الاعتبار قيود الجدول الزمني.

  1. بعد إنشائها ، لا يمكنك تطبيق أوامر DDL على الجداول الرئيسية أو التاريخية. ولا يمكنك حذف الجدول الزمني
  2. لا يمكنك تغيير البيانات في الجدول التاريخي

القيد الثاني يناسبنا ، ولكن ماذا نفعل مع الأول؟

الخوارزمية هي كما يلي:

 --        ALTER TABLE dbo.VersionControl SET ( SYSTEM_VERSIONING = OFF ); /* -  */ --    : ALTER TABLE dbo.VersionControl SET ( SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.VersionControlHistory, DATA_CONSISTENCY_CHECK = OFF ); 

على الرغم من عدم وجود أي فهارس على الجدول حتى الآن ، املأها بإجراءاتنا ووظائفنا ، وما إلى ذلك INIT المميز ، والذي يعني في حالتنا الموضع الأولي

 DECLARE @query NVARCHAR(MAX), @template NVARCHAR(MAX) = N' USE [db] INSERT INTO MASTER.dbo.VersionControl WITH (TABLOCKX) ( Event, Db, Sch, Object, Sql, Login ) SELECT ''INIT'' AS Event, DB_NAME(), ss.name AS Sch, so.name AS Object, CONCAT(''<query><![CDATA['', sasm.definition, '']]></query>'' ), SUSER_SNAME() AS Login FROM sys.objects AS so JOIN sys.schemas AS ss ON ss.schema_id = so.schema_id JOIN sys.all_sql_modules AS sasm ON sasm.object_id = so.object_id WHERE so.is_ms_shipped = 0 AND NOT EXISTS ( SELECT 1 FROM MASTER.dbo.VersionControl AS vc WHERE vc.Db = ''[db]'' AND vc.Sch = ss.name AND vc.Object = so.name ); '; DECLARE @databases TABLE (rn INT, Name sysname); INSERT @databases (rn, Name) SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn, name FROM sys.databases WHERE owner_sid != 0x01; DECLARE @i INT = 1, @max INT = (SELECT MAX(rn) FROM @databases), @error NVARCHAR(128), @db sysname; WHILE @i < @max BEGIN SELECT @query = REPLACE(@template, '[db]', Name), @db = Name FROM @databases WHERE rn = @i; BEGIN TRY EXECUTE sp_executesql @query; SET @i += 1; CONTINUE; END TRY BEGIN CATCH SET @error = CONCAT( 'XML Parsing error. In this case that''s mean one of [', @db, '] object is invalid for convert to XML' ); PRINT @error; SET @i += 1; CONTINUE; END CATCH; END; GO 

لأن ستحدث التغييرات على الكائنات مع عبارة UPDATE ، وسننظر غالبًا إلى الإصدارات حسب المفتاح: قاعدة البيانات والمخطط واسم الكائن ، الفهرس يستدعي!

 IF NOT EXISTS ( SELECT 1 FROM sys.indexes WHERE name = 'IX_VersionControl_upd_key' ) CREATE UNIQUE NONCLUSTERED INDEX IX_VersionControl_upd_key ON MASTER.dbo.VersionControl (Db, Sch, Object) INCLUDE (Sql, Event, Login); 

كل شيء جاهز لبدء تخزين الإصدارات وسيساعدنا في مشغل DDL هذا

هام! لأن توجد جداول الإصدارات في قاعدة البيانات الرئيسية ، بعد إنشاء مشغل ، لا يمكن لأي شخص ليس لديه حقوق في قاعدة البيانات هذه تعديل الكائنات أو إنشاؤها أو حذفها

 IF EXISTS ( SELECT 1 FROM sys.server_triggers WHERE name = 'tr_VersionControl' ) DROP TRIGGER tr_VersionControl ON ALL SERVER GO CREATE TRIGGER tr_VersionControl ON ALL SERVER --WITH ENCRYPTION --   /*      : https://docs.microsoft.com/ru-ru/sql/relational-databases/triggers/ddl-events?view=sql-server-2017 */ FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE, CREATE_VIEW, ALTER_VIEW, DROP_VIEW, CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION, CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE, CREATE_ASSEMBLY, ALTER_ASSEMBLY, DROP_ASSEMBLY, CREATE_INDEX, ALTER_INDEX, DROP_INDEX, CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER, RENAME AS BEGIN SET NOCOUNT ON; UPDATE vs SET vs.Event = ev.EventType, vs.Sql = CONCAT('<query><!CDATA', ev.Sql, '></query>' ), vs.Login = ev.Login FROM MASTER.dbo.VersionControl AS vs JOIN ( SELECT * FROM ( VALUES ( EVENTDATA().value( '(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(128)' ), EVENTDATA().value( '(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(128)' ), EVENTDATA().value( '(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(128)' ), EVENTDATA().value( '(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)' ), EVENTDATA().value( '(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(128)' ) )) AS Event (EventType, Sch, Object, Sql, Login ) ) ev ON vs.Db = DB_NAME() AND vs.Sch = ev.Sch AND vs.Object = ev.Object ; END GO 

ولراحة استخدام هذا النظام ، تم اقتراح الإجراء أدناه.

إنه سهل الاستخدام. سوف تساعدنا البادئة sp_ في الوصول إلى الإجراء دون تحديد قاعدة البيانات والمخطط. يتم تعبئة المعلمات بشكل حدسي. يمكنك تحديد قاعدة البيانات فقط وسنرى الكائنات المرتبطة بها فقط طوال الوقت ، ولكن يمكنك أيضًا استخدام المخطط والكائن نفسه ، وبالطبع النطاق الزمني الذي تم إجراء التغييرات عليه.

 CREATE PROCEDURE dbo.sp_Vc @db sysname = '%', @sch sysname = '%', @obj sysname = '%', @from DATETIME2(0) = NULL, @to DATETIME2(0) = NULL AS BEGIN SET NOCOUNT ON; IF @from IS NULL AND @to IS NULL BEGIN SELECT * FROM master.dbo.VersionControl WHERE Db LIKE @db AND Sch LIKE @sch AND Object LIKE @obj ORDER BY StartDate DESC END ELSE BEGIN SELECT * FROM master.dbo.VersionControl FOR SYSTEM_TIME BETWEEN @from AND @to WHERE Db LIKE @db AND Sch LIKE @sch AND Object LIKE @obj ORDER BY StartDate DESC END END GO 

فيما يلي أمثلة على استخدام الإجراء

 --        sp_Vc; /*          */ sp_Vc 'dwh'; /*            */ sp_Vc 'dwh', 'dbo'; /*      ,        */ sp_Vc 'dwh', 'dbo', 'MyObject'; /*      , ,       1-  9-  */ sp_Vc 'dwh', 'dbo', 'MyObject', '20180501 00:00:00', '20180509 00:00:00'; 

يمكنك تثبيت هذا الإطار المصغر من مستودعي ، وإذا كان إصدار SQL Sever الخاص بك أصغر من 2016 ، فأنت هنا . بالمناسبة ، نحن نستخدم هذا الإصدار الآن ، ولكنه ليس رائعًا.

في الختام


لم أتمكن أبدًا من هزيمة الاستنتاج . و & _lt ؛ بدلاً من العلامات > و < من الجدول master.dbo.VersionControl حسب الحقل Sql . إذا كنت تستطيع المساعدة في هذا أو لديك أي أفكار ، فأنا في انتظار طلب سحب .

شكرا لوقتك ، وضع النجوم والقلوب والسهام.

Source: https://habr.com/ru/post/ar419919/


All Articles