[MS-SQL] 프로시저 자동 백업.

프로시저를 수정하다보면 간혹 실수를 하거나 이전 구문으로 복원을 해야할 때 가있다.

데이터베이스에 트리거를 등록해두고

프로시저에 CREATE나 ALTER등이 될 때 마다 소스를 지정한 테이블에 백업하는 방법이다.

CREATE TABLE dbo.SPLOG(
 idx bigint IDENTITY(1,1) NOT NULL,
 sp_name varchar(100) NULL,
 division varchar(20) NULL,
 SQLCMD varchar(max) NULL,
 author varchar(20) NULL,
 modified_date datetime NULL,
 CONSTRAINT XPKSPLOG PRIMARY KEY NONCLUSTERED
(
 idx ASC
))

GO


CREATE TRIGGER TRG_SPLOG ON DATABASE
FOR
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER

AS

DECLARE @DATA XML

SET @DATA = EVENTDATA()

INSERT INTO DBO.SPLOG (sp_name, division, SQLCMD, author, modified_date)
VALUES
(@DATA.value('(/EVENT_INSTANCE/ObjectName)[1]', 'VARCHAR(100)'),
 @DATA.value('(/EVENT_INSTANCE/EventType)[1]', 'VARCHAR(100)'),
 @DATA.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'VARCHAR(MAX)'),
 HOST_NAME(),
 GETDATE())

이제 수정이 이루어질때마다 SPLOG 테이블에 이력이 남게될 것이다.

  • 추가

프로시저의 문장 길이가 varchar(max)가 허용하는 길이를 넘어서는 경우가 있다.

그럴 경우엔 한도 길이만큼 잘라서 분할해서 저장하면 온전히 보관할 수 있다.

프로시저 로그 저장용 테이블 생성

USE [DB]
GO

/****** Object:  Table [dbo].[SPLOG]    Script Date: 2021-06-03 오전 11:04:17 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[SPLOG](
  [idx] [bigint] IDENTITY(1,1) NOT NULL,
  [sp_name] [varchar](100) NULL,
  [division] [varchar](20) NULL,
  [SQLCMD] [nvarchar](max) NULL,
  [author] [varchar](20) NULL,
  [modified_date] [datetime] NULL,
  [page] [int] NULL,
  [max_page] [int] NULL,
 CONSTRAINT [XPKSPLOG] PRIMARY KEY NONCLUSTERED 
(
  [idx] 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

SET ANSI_PADDING OFF
GO

트리거 생성

USE [DB]
GO

/****** Object:  DdlTrigger [TRG_SPLOG]    Script Date: 2021-06-03 오전 11:00:37 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


CREATE TRIGGER [TRG_SPLOG] ON DATABASE
FOR
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER

AS

DECLARE @DATA XML
DECLARE @NUM INT, @MAX INT,@INTERVAL INT, @INDEX INT, @TMPTEXT VARCHAR(MAX), @MERGETEXT VARCHAR(MAX), @SP_NAME VARCHAR(100), @LOG_COUNT INT

SET @DATA = EVENTDATA()

SET @SP_NAME = @DATA.value('(/EVENT_INSTANCE/ObjectName)[1]', 'VARCHAR(100)')
SET @NUM = 1
--해당 프로시저의 최대 페이지를 가져온다
SET @MAX = (SELECT max(colid)
FROM syscomments a,
  sysobjects b
where B.xtype  in ('P','FN')
AND A.ID = B.ID
and b.name = @SP_NAME)

SET @INTERVAL = 1
SET @INDEX = 1
SET @TMPTEXT = ''
SET @MERGETEXT = ''

IF @MAX > 9 --페이지수가 9페이지를 넘는다면 분할해서 저장하자
  BEGIN
    WHILE @NUM<=@MAX
      BEGIN   
          SET @TMPTEXT = (SELECT A.TEXT
          FROM syscomments a,
            sysobjects b
          where B.xtype  in ('P','FN')
          AND A.ID = B.ID
          and b.name = @SP_NAME
          and colid = @NUM)

          IF @NUM = 1
            BEGIN
              SET @TMPTEXT = REPLACE(@TMPTEXT, 'CREATE', 'ALTER')
            END

          SET @MERGETEXT = @MERGETEXT + @TMPTEXT

          SET @NUM = @NUM + 1
          SET @INTERVAL = @INTERVAL + 1

          IF @INTERVAL = 9 OR @NUM>@MAX
          BEGIN
            --SELECT @MERGETEXT, @INDEX
            INSERT INTO DBO.SPLOG (sp_name, division, SQLCMD, author, modified_date, page, max_page)
            VALUES
            (@DATA.value('(/EVENT_INSTANCE/ObjectName)[1]', 'VARCHAR(100)'),
             @DATA.value('(/EVENT_INSTANCE/EventType)[1]', 'VARCHAR(100)'),
             @MERGETEXT,
             HOST_NAME(),
             GETDATE(),
             @INDEX,
             @MAX/9 + 1)
            SET @INTERVAL = 1
            SET @INDEX = @INDEX + 1
            SET @MERGETEXT = ''
          END
      END;
  END
ELSE
  BEGIN
    INSERT INTO DBO.SPLOG (sp_name, division, SQLCMD, author, modified_date, page, max_page)
    VALUES
    (@DATA.value('(/EVENT_INSTANCE/ObjectName)[1]', 'VARCHAR(100)'),
     @DATA.value('(/EVENT_INSTANCE/EventType)[1]', 'VARCHAR(100)'),
     @DATA.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'VARCHAR(MAX)'),
     HOST_NAME(),
     GETDATE(),
     @INDEX,
     @INDEX)
  END

--저장된로그가 200개가 넘으면 이전걸 지워주자
SET @LOG_COUNT = (select count(*) from SPLOG where sp_name = @SP_NAME)

IF @LOG_COUNT > 200 
  BEGIN
    delete from SPLOG
    where sp_name = @SP_NAME
    and idx not in (select top 200 idx from SPLOG
    where sp_name = @SP_NAME
    order by modified_date desc)
  END


GO

SET ANSI_NULLS OFF
GO

SET QUOTED_IDENTIFIER OFF
GO

ENABLE TRIGGER [TRG_SPLOG] ON DATABASE
GO