用于检查 SQL 服务器实例更改的脚本?
Script to check changes on SQL Server Instance?
是否有任何 SQL
脚本来检查其他数据库管理员对 SQL Server
实例所做的更改,我收到有关该更改的电子邮件警报。如果是,请向我提供脚本和应用它的所有步骤。我正在使用 SQL Server 2012。
一个解决方案是使用 DDL trigger
来捕获所有模式更改(过程、函数、table 定义等)。这适用于所有非加密对象,当然,其他管理员不得禁用它。
可以找到有关如何编写此类触发器和持久更改的更多详细信息here。
[稍后编辑]
我记得我在开发环境上创建了这样的审计,我可以根据指示的文章提供自定义版本。除了通常的信息外,我还在旧对象文本和新对象文本之间添加了一个 "distance",以便对变化幅度有一个基本的了解。
1) Table定义:
IF OBJECT_ID('dbo.DDLEvents', 'U') IS NULL
BEGIN
CREATE TABLE dbo.DDLEvents
(
EventId INT IDENTITY(1, 1) NOT NULL,
EventDate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
EventType NVARCHAR(64),
EventXML XML,
DatabaseName NVARCHAR(255),
SchemaName NVARCHAR(255),
ObjectName NVARCHAR(255),
HostName VARCHAR(128),
IPAddress VARCHAR(32),
ProgramName NVARCHAR(255),
LoginName NVARCHAR(255),
ObjectDefinition NVARCHAR(MAX),
LastObjDefinition NVARCHAR(MAX),
Diff INT -- edit distance between last and current object version (gives an idea of how much was changed in the object)
);
create index IDX_DDLEvents_Object ON DDLEvents (SchemaName, ObjectName)
END
go
2) 现有对象的初始文本:
IF NOT EXISTS(SELECT * FROM dbo.DDLEvents)
BEGIN
INSERT INTO dbo.DDLEvents
(EventType, DatabaseName, SchemaName, ObjectName, LoginName, ObjectDefinition)
SELECT 'CREATE_PROCEDURE', DB_NAME(), OBJECT_SCHEMA_NAME([object_id]), OBJECT_NAME([object_id]), 'SYSTEM', OBJECT_DEFINITION([object_id])
FROM sys.procedures;
INSERT INTO dbo.DDLEvents
(EventType, DatabaseName, SchemaName, ObjectName, LoginName, ObjectDefinition)
SELECT 'CREATE_VIEW', DB_NAME(), OBJECT_SCHEMA_NAME([object_id]), OBJECT_NAME([object_id]), 'SYSTEM', OBJECT_DEFINITION([object_id])
FROM sys.views;
INSERT INTO dbo.DDLEvents
(EventType, DatabaseName, SchemaName, ObjectName, LoginName, ObjectDefinition)
SELECT 'CREATE_FUNCTION', DB_NAME(), OBJECT_SCHEMA_NAME([object_id]), OBJECT_NAME([object_id]), 'SYSTEM', OBJECT_DEFINITION([object_id])
FROM sys.objects
-- scalar, inline table-valued, table-valued
WHERE type IN ('FN', 'IF', 'TF')
END
go
3)距离函数(CLR):
[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = false)]
public static int Levenshtein(SqlString S1, SqlString S2)
{
if (S1.IsNull)
S1 = new SqlString("");
if (S2.IsNull)
S2 = new SqlString("");
int maxLen = 4096;
// keeping only the first part of the string (performance reasons)
String SC1 = S1.Value.ToUpper();
String SC2 = S2.Value.ToUpper();
if (SC1.Length > maxLen)
SC1 = SC1.Remove(maxLen);
if (SC2.Length > maxLen)
SC2 = SC2.Remove(maxLen);
int n = SC1.Length;
int m = SC2.Length;
short[,] d = new short[n + 1, m + 1];
int cost = 0;
if (n + m == 0)
{
return 0;
}
else if (n == 0)
{
return 0;
}
else if (m == 0)
{
return 0;
}
for (short i = 0; i <= n; i++)
d[i, 0] = i;
for (short j = 0; j <= m; j++)
d[0, j] = j;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (SC1[i - 1] == SC2[j - 1])
cost = 0;
else
cost = 1;
d[i, j] = (short) System.Math.Min(System.Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
}
}
// double percentage = System.Math.Round((1.0 - ((double)d[n, m] / (double)System.Math.Max(n, m))) * 100.0, 2);
// return percentage;
return d[n, m];
}
4) DDL触发器定义
if not exists (select * from sys.triggers where name = 'DDL_Audit_Trigger')
exec ('create trigger DDL_Audit_Trigger ON DATABASE FOR CREATE_PROCEDURE AS BEGIN PRINT 1; END')
GO
ALTER TRIGGER [DDL_Audit_Trigger]
ON DATABASE
FOR
CREATE_ASSEMBLY, ALTER_ASSEMBLY, DROP_ASSEMBLY,
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_ROLE, ALTER_ROLE, DROP_ROLE,
CREATE_SCHEMA, ALTER_SCHEMA, DROP_SCHEMA,
CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_TYPE, DROP_TYPE,
CREATE_USER, ALTER_USER, DROP_USER,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
RENAME
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
DECLARE @EventData XML = EVENTDATA();
DECLARE @ip VARCHAR(32) = ( SELECT client_net_address FROM sys.dm_exec_connections WHERE session_id = @@SPID);
DECLARE @ObjectSchema NVARCHAR(255) = @EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)')
DECLARE @ObjectName NVARCHAR(255) = @EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)')
-- DECLARE @ObjectFullName NVARCHAR(255) = @ObjectSchema + '.' + @ObjectName
DECLARE @CommandText NVARCHAR(MAX) = @EventData.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)')
DECLARE @LastObjectChange DATETIME = (SELECT TOP 1 EventDate FROM dbo.DDLEvents where SchemaName = @ObjectSchema and ObjectName = @ObjectName ORDER BY EventDate DESC)
DECLARE @LastObjectDefinition NVARCHAR(MAX) = (SELECT TOP 1 ObjectDefinition FROM dbo.DDLEvents where SchemaName = @ObjectSchema and ObjectName = @ObjectName and EventDate = @LastObjectChange ORDER BY EventDate DESC)
INSERT INTO dbo.DDLEvents
(EventType,
EventXML, DatabaseName,
SchemaName, ObjectName,
HostName, IPAddress, ProgramName, LoginName,
ObjectDefinition, LastObjDefinition, Diff
)
SELECT @EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(100)'),
@EventData, DB_NAME(),
@ObjectSchema, @ObjectName,
HOST_NAME(), @ip, PROGRAM_NAME(), SUSER_SNAME(),
@CommandText, @LastObjectDefinition, dbo.Levenshtein(@CommandText, @LastObjectDefinition);
END TRY
BEGIN CATCH
INSERT INTO dbo.DDLEventsLog (Error)
SELECT ERROR_MESSAGE()
END CATCH
END
GO
是否有任何 SQL
脚本来检查其他数据库管理员对 SQL Server
实例所做的更改,我收到有关该更改的电子邮件警报。如果是,请向我提供脚本和应用它的所有步骤。我正在使用 SQL Server 2012。
一个解决方案是使用 DDL trigger
来捕获所有模式更改(过程、函数、table 定义等)。这适用于所有非加密对象,当然,其他管理员不得禁用它。
可以找到有关如何编写此类触发器和持久更改的更多详细信息here。
[稍后编辑]
我记得我在开发环境上创建了这样的审计,我可以根据指示的文章提供自定义版本。除了通常的信息外,我还在旧对象文本和新对象文本之间添加了一个 "distance",以便对变化幅度有一个基本的了解。
1) Table定义:
IF OBJECT_ID('dbo.DDLEvents', 'U') IS NULL
BEGIN
CREATE TABLE dbo.DDLEvents
(
EventId INT IDENTITY(1, 1) NOT NULL,
EventDate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
EventType NVARCHAR(64),
EventXML XML,
DatabaseName NVARCHAR(255),
SchemaName NVARCHAR(255),
ObjectName NVARCHAR(255),
HostName VARCHAR(128),
IPAddress VARCHAR(32),
ProgramName NVARCHAR(255),
LoginName NVARCHAR(255),
ObjectDefinition NVARCHAR(MAX),
LastObjDefinition NVARCHAR(MAX),
Diff INT -- edit distance between last and current object version (gives an idea of how much was changed in the object)
);
create index IDX_DDLEvents_Object ON DDLEvents (SchemaName, ObjectName)
END
go
2) 现有对象的初始文本:
IF NOT EXISTS(SELECT * FROM dbo.DDLEvents)
BEGIN
INSERT INTO dbo.DDLEvents
(EventType, DatabaseName, SchemaName, ObjectName, LoginName, ObjectDefinition)
SELECT 'CREATE_PROCEDURE', DB_NAME(), OBJECT_SCHEMA_NAME([object_id]), OBJECT_NAME([object_id]), 'SYSTEM', OBJECT_DEFINITION([object_id])
FROM sys.procedures;
INSERT INTO dbo.DDLEvents
(EventType, DatabaseName, SchemaName, ObjectName, LoginName, ObjectDefinition)
SELECT 'CREATE_VIEW', DB_NAME(), OBJECT_SCHEMA_NAME([object_id]), OBJECT_NAME([object_id]), 'SYSTEM', OBJECT_DEFINITION([object_id])
FROM sys.views;
INSERT INTO dbo.DDLEvents
(EventType, DatabaseName, SchemaName, ObjectName, LoginName, ObjectDefinition)
SELECT 'CREATE_FUNCTION', DB_NAME(), OBJECT_SCHEMA_NAME([object_id]), OBJECT_NAME([object_id]), 'SYSTEM', OBJECT_DEFINITION([object_id])
FROM sys.objects
-- scalar, inline table-valued, table-valued
WHERE type IN ('FN', 'IF', 'TF')
END
go
3)距离函数(CLR):
[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = false)]
public static int Levenshtein(SqlString S1, SqlString S2)
{
if (S1.IsNull)
S1 = new SqlString("");
if (S2.IsNull)
S2 = new SqlString("");
int maxLen = 4096;
// keeping only the first part of the string (performance reasons)
String SC1 = S1.Value.ToUpper();
String SC2 = S2.Value.ToUpper();
if (SC1.Length > maxLen)
SC1 = SC1.Remove(maxLen);
if (SC2.Length > maxLen)
SC2 = SC2.Remove(maxLen);
int n = SC1.Length;
int m = SC2.Length;
short[,] d = new short[n + 1, m + 1];
int cost = 0;
if (n + m == 0)
{
return 0;
}
else if (n == 0)
{
return 0;
}
else if (m == 0)
{
return 0;
}
for (short i = 0; i <= n; i++)
d[i, 0] = i;
for (short j = 0; j <= m; j++)
d[0, j] = j;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (SC1[i - 1] == SC2[j - 1])
cost = 0;
else
cost = 1;
d[i, j] = (short) System.Math.Min(System.Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
}
}
// double percentage = System.Math.Round((1.0 - ((double)d[n, m] / (double)System.Math.Max(n, m))) * 100.0, 2);
// return percentage;
return d[n, m];
}
4) DDL触发器定义
if not exists (select * from sys.triggers where name = 'DDL_Audit_Trigger')
exec ('create trigger DDL_Audit_Trigger ON DATABASE FOR CREATE_PROCEDURE AS BEGIN PRINT 1; END')
GO
ALTER TRIGGER [DDL_Audit_Trigger]
ON DATABASE
FOR
CREATE_ASSEMBLY, ALTER_ASSEMBLY, DROP_ASSEMBLY,
CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_ROLE, ALTER_ROLE, DROP_ROLE,
CREATE_SCHEMA, ALTER_SCHEMA, DROP_SCHEMA,
CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_TYPE, DROP_TYPE,
CREATE_USER, ALTER_USER, DROP_USER,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
RENAME
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
DECLARE @EventData XML = EVENTDATA();
DECLARE @ip VARCHAR(32) = ( SELECT client_net_address FROM sys.dm_exec_connections WHERE session_id = @@SPID);
DECLARE @ObjectSchema NVARCHAR(255) = @EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)')
DECLARE @ObjectName NVARCHAR(255) = @EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)')
-- DECLARE @ObjectFullName NVARCHAR(255) = @ObjectSchema + '.' + @ObjectName
DECLARE @CommandText NVARCHAR(MAX) = @EventData.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)')
DECLARE @LastObjectChange DATETIME = (SELECT TOP 1 EventDate FROM dbo.DDLEvents where SchemaName = @ObjectSchema and ObjectName = @ObjectName ORDER BY EventDate DESC)
DECLARE @LastObjectDefinition NVARCHAR(MAX) = (SELECT TOP 1 ObjectDefinition FROM dbo.DDLEvents where SchemaName = @ObjectSchema and ObjectName = @ObjectName and EventDate = @LastObjectChange ORDER BY EventDate DESC)
INSERT INTO dbo.DDLEvents
(EventType,
EventXML, DatabaseName,
SchemaName, ObjectName,
HostName, IPAddress, ProgramName, LoginName,
ObjectDefinition, LastObjDefinition, Diff
)
SELECT @EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(100)'),
@EventData, DB_NAME(),
@ObjectSchema, @ObjectName,
HOST_NAME(), @ip, PROGRAM_NAME(), SUSER_SNAME(),
@CommandText, @LastObjectDefinition, dbo.Levenshtein(@CommandText, @LastObjectDefinition);
END TRY
BEGIN CATCH
INSERT INTO dbo.DDLEventsLog (Error)
SELECT ERROR_MESSAGE()
END CATCH
END
GO