Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Coleta Automática de Dados de Alterações de Esquema de Banco de Dados no MS SQL Server

Introdução


Você já enfrentou uma situação em que precisa fazer alterações em um procedimento armazenado ou em uma visualização muito rapidamente? Tenho, muitas vezes, especialmente na fase de implementação. Infelizmente, um sistema de controle de versão não pode ajudar neste caso. Ainda assim, como eu poderia entender que algo foi modificado, e quando?

Este artigo descreve uma possível solução para coleta automática de dados sobre alterações de esquema de banco de dados no MS SQL Server. Como de costume, ficarei feliz em ouvir quaisquer soluções alternativas.


Solução

  1. Crie duas tabelas:a primeira será para cada banco de dados, a segunda – para todos os bancos de dados:
    USE [DATABASE_NAME]
    GO
    
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE TABLE [srv].[ddl_log](
        [DDL_Log_GUID] [uniqueidentifier] NOT NULL,
        [PostTime] [datetime] NOT NULL,
        [DB_Login] [nvarchar](255) NULL,
        [DB_User] [nvarchar](255) NULL,
        [Event] [nvarchar](255) NULL,
        [TSQL] [nvarchar](max) NULL,
     CONSTRAINT [PK_ddl_log] PRIMARY KEY CLUSTERED 
    (
        [DDL_Log_GUID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    
    ALTER TABLE [srv].[ddl_log] ADD  CONSTRAINT [DF_ddl_log_DDL_Log_GUID]  DEFAULT (newid()) FOR [DDL_Log_GUID]
    GO
    
    USE [DATABASE_NAME]
    GO
    
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE TABLE [srv].[ddl_log_all](
        [DDL_Log_GUID] [uniqueidentifier] NOT NULL,
        [Server_Name] [nvarchar](255) NOT NULL,
        [DB_Name] [nvarchar](255) NOT NULL,
        [PostTime] [datetime] NOT NULL,
        [DB_Login] [nvarchar](255) NULL,
        [DB_User] [nvarchar](255) NULL,
        [Event] [nvarchar](255) NULL,
        [TSQL] [nvarchar](max) NULL,
        [InsertUTCDate] [datetime] NOT NULL,
     CONSTRAINT [PK_ddl_log_all] PRIMARY KEY CLUSTERED 
    (
        [DDL_Log_GUID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    
    ALTER TABLE [srv].[ddl_log_all] ADD  CONSTRAINT [DF_ddl_log_all_DDL_Log_GUID]  DEFAULT (newid()) FOR [DDL_Log_GUID]
    GO
    
    ALTER TABLE [srv].[ddl_log_all] ADD  CONSTRAINT [DF_ddl_log_all_InsertUTCDate]  DEFAULT (getutcdate()) FOR [InsertUTCDate]
    GO
  2. Crie um DDL-trigger para um banco de dados que colete mudanças de esquema:
    USE [DATABASE_NAME]
    GO
    
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE TRIGGER [SchemaLog] 
    ON DATABASE --ALL SERVER 
    FOR DDL_DATABASE_LEVEL_EVENTS 
    AS
        SET NOCOUNT ON;
        SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
        DECLARE @data XML
        begin try
        if(CURRENT_USER<>'NT AUTHORITY\NETWORK SERVICE' and SYSTEM_USER<>'NT AUTHORITY\NETWORK SERVICE')
        begin
            SET @data = EVENTDATA();
            INSERT srv.ddl_log(
                        PostTime,
                        DB_Login,
                        DB_User,
                        Event,
                        TSQL
                      ) 
            select 
                        GETUTCDATE(),
                        CONVERT(nvarchar(255), SYSTEM_USER),
                        CONVERT(nvarchar(255), CURRENT_USER), 
                        @data.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(255)'), 
                        @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)')
            where       @data.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(255)') not in('UPDATE_STATISTICS', 'ALTER_INDEX')
                    and @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)') not like '%Msmerge%'; 
                        --there is no need in tracking changes of replication objects
        end
        end try
        begin catch
        end catch
    
    GO
    
    SET ANSI_NULLS OFF
    GO
    
    SET QUOTED_IDENTIFIER OFF
    GO
    
    ENABLE TRIGGER [SchemaLog] ON DATABASE
    GO

Eu recomendo ajustar um filtro e não fazer um DDL-trigger para todo o servidor. É inútil, pois você obterá muitas informações desnecessárias. Nesse caso, é melhor criar um gatilho para cada banco de dados.
No entanto, você terá que desativar esse gatilho durante operações complicadas, por exemplo, replicação. Mas mais tarde, você poderá ligá-lo novamente.
  1. Você precisará reunir informações em uma única tabela. Por exemplo, você pode fazer isso com uma tarefa no SQL Server Agent uma vez por semana.
  2. É possível reunir tudo em uma tabela de outra forma que você preferir.

Além disso, recomendo excluir dados antigos.

Resultado


Neste artigo, analisei um exemplo de implementação de uma coleta automática de dados sobre alterações de esquemas de bancos de dados no MS SQL Server. Ele nos permite descobrir o que e quando foi modificado e, se necessário, revertê-los. Em geral, esta solução pode ser útil na fase de implementação onde há muitos erros e quando temos diferentes versões de cópias de bancos de dados a serem analisadas. Se você deseja descobrir o motivo das alterações, pode fazê-lo recuperando um histórico de revisões.

Leia também:


Coleta Automática de Dados sobre Tarefas Concluídas no MS SQL Server