3

我正在尝试为在 Web 应用程序、SQL Server 代理作业和手动运行对数据库的查询中执行的操作创建审计跟踪。我正在尝试使用触发器来捕获某些表上的更新、插入和删除。

总的来说,这个过程是有效的。例如,用户在 Web 应用程序中执行更新,触发器将更新的数据写入我定义的审计跟踪表,包括执行操作的人的用户名。从 Web 应用程序或手动查询的角度来看,这工作得很好,但我们也有几十个 SQL Server 代理作业,我想捕获哪个运行了特定的查询。每个代理作业都使用相同的用户名运行。这也可以正常工作并将用户名正确输入到表中,但我找不到哪个作业调用此查询。

我当前的“解决方案”是找出触发时当前正在运行的作业,因为其中之一必须是正确的。使用:

CREATE TABLE #xp_results 

    ( 
    job_id                UNIQUEIDENTIFIER NOT NULL,   
    last_run_date         INT              NOT NULL,   
    last_run_time         INT              NOT NULL,   
    next_run_date         INT              NOT NULL,   
    next_run_time         INT              NOT NULL,   
    next_run_schedule_id  INT              NOT NULL,   
    requested_to_run      INT              NOT NULL, -- BOOL   
    request_source        INT              NOT NULL,   
    request_source_id     sysname          COLLATE database_default NULL,   
    running               INT              NOT NULL, -- BOOL   
    current_step          INT              NOT NULL,   
    current_retry_attempt INT              NOT NULL,   
    job_state             INT              NOT NULL
    )   

INSERT INTO  #xp_results  
EXECUTE master.dbo.xp_sqlagent_enum_jobs 1, 'sa'  
SELECT @runningJobs = STUFF((SELECT ',' + j.name 
                FROM #xp_results r
                INNER JOIN msdb..sysjobs j ON r.job_id = j.job_id
                WHERE running = 1
                FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')

DROP TABLE #xp_results

我运行了一个特定的作业来测试它似乎可以工作,因为任何正在运行的 OTHER 作业都将列在 中@runningJobs,但它不会记录运行它的作业。我假设当触发器运行时,作业已经完成。

有没有办法我可以找出什么工作调用了触发触发器的查询?

编辑:我尝试更改SELECT上面的查询以获取在过去 2 分钟内运行或当前正在运行的任何作业。SQL 查询现在是:

SELECT @runningJobs = STUFF((SELECT ',' + j.name 
            FROM #xp_results r
            INNER JOIN msdb..sysjobs j ON r.job_id = j.job_id
            WHERE (last_run_date = CAST(REPLACE(LEFT(CONVERT(VARCHAR, getdate(), 120), 10), '-', '') AS INT)
            AND last_run_time > CAST(REPLACE(LEFT(CONVERT(VARCHAR,getdate(),108), 8), ':', '') AS INT) - 200)
            OR running = 1
            FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')

当我运行作业时,然后在作业运行时运行上述查询,将返回正确的作业。但是,当 SSIS 包运行时,无论是通过 SQL Server 代理作业还是在 SSIS 中手动运行,@runningJobs都不会填充并仅返回NULL.

所以我现在认为这是 SSIS 和master.dbo.xp_sqlagent_enum_jobs. 还有其他想法吗?

编辑#2:实际上不要认为这是权限错误。此代码下方有一条INSERT语句,如果它是权限错误,则该INSERT语句不会运行,因此审核行不会添加到数据库中。因此,由于数据库中添加了一行,只是没有runningJobs填充该字段。奇怪的时代。

编辑#3:我只是想澄清一下,我正在寻找一个不需要我进入每项工作并改变任何东西的解决方案。有太多的工作使这成为一个可行的解决方案。

4

3 回答 3

2

工作代码在第一次编辑- (anothershrubery

在审核触发器中使用app_name()函数http://msdn.microsoft.com/en-us/library/ms189770.aspx来获取运行查询的应用程序的名称。

对于 SQL 代理作业,app_name 在应用名称中包含作业步骤 ID(如果是T-SQL步骤)。我们在审计触发器中这样做并且效果很好。app_name()从审计触发器中运行时的结果示例:

SQLAgent - TSQL JobStep(作业 0x96EB56A24786964889AB504D9A920D30:步骤 1)

job_id可以通过 中的列查找此作业msdb.dbo.sysjobs_view

由于 SSIS 包在 SQL 代理作业引擎之外启动 SQL 连接,因此这些连接将有自己的应用程序名称,您需要在 SSIS 包的连接字符串中设置应用程序名称。在 SSIS 包、Web 应用程序、WinForms 或任何连接到 SQL Server 的客户端中,您可以通过在连接字符串中使用它来设置 app_name 函数返回的值:

"Application Name=MyAppNameGoesHere;" 

http://www.connectionstrings.com/use-application-name-sql-server/

如果“应用程序名称”未在 .NET 连接字符串中设置,则使用时的默认值为System.Data.SqlClient.SqlConnection“.Net SqlClient 数据提供程序”。

其他一些常用于审计的字段:

以下是用于设置/获取上下文信息的 SQL 辅助方法:

CREATE PROC dbo.usp_ContextInfo_SET
    @val varchar(128)
as
begin
    set nocount on;
    DECLARE @c varbinary(128);
    SET @c=cast(@val as varbinary(128));
    SET CONTEXT_INFO @c;
end
GO

CREATE FUNCTION [dbo].[ufn_ContextInfo_Get] ()
RETURNS varchar(128)
AS
BEGIN
    --context_info is binary data type, so will pad any values will CHAR(0) to the end of 128 bytes, so need to replace these with empty string.
    RETURN REPLACE(CAST(CONTEXT_INFO() AS varchar(128)), CHAR(0), '')
END

编辑:

app_name() 是获取查询中涉及的应用程序的首选方式,但是由于您不想更新任何 SSIS 包,因此这里有一个更新的查询,用于使用以下记录的 SQL 代理获取当前正在执行的作业表。您可能必须在 msdb 数据库中为这些表调整 SELECT 的 GRANT 以使查询成功,或者使用此查询创建视图,并调整该视图的授权。

询问:

;with cteSessions as
(
    --each time that SQL Agent is started, a new record is added to this table.
    --The most recent session is the current session, and prior sessions can be used 
    --to identify the job state at the time that SQL Agent is restarted or stopped unexpectedly
    select top 1 s.session_id
    from msdb.dbo.syssessions s
    order by s.agent_start_date desc
)
SELECT runningJobs =
    STUFF(
    (   SELECT N', [' + j.name + N']'
        FROM msdb.dbo.sysjobactivity a
            inner join cteSessions s on s.session_id = a.session_id
            inner join msdb.dbo.sysjobs j on a.job_id = j.job_id
            left join msdb.dbo.sysjobhistory h2 on h2.instance_id = a.job_history_id
        WHERE 
            --currently executing jobs:
            h2.instance_id is null
            AND a.start_execution_date is not null
            AND a.stop_execution_date is null
        ORDER BY j.name
        FOR XML PATH(''), ROOT('root'), TYPE
    ).query('root').value('.', 'nvarchar(max)') --convert the xml to nvarchar(max)
    , 1, 2, '') -- replace the leading comma and space with empty string.
;

编辑#2:

此外,如果您使用的是 SQL 2012 或更高版本,请SSISDB.catalog.executions查看http://msdn.microsoft.com/en-us/library/ff878089(v=sql.110).aspx以获取当前正在运行的 SSIS 包的列表,无论它们是否是从计划的作业中开始的。我在 2012 年之前的 SQL Server 版本中没有看到等效视图。

于 2014-05-20T10:56:50.123 回答
0

我会在您的表中添加一个额外的列,例如 Update_Source,并让所有源应用程序(包括 SSIS)在更新表时设置它。

您可以使用 USER 作为该列的默认值,以最大限度地减少所需的更改。

于 2014-05-15T01:50:19.013 回答
0

您可以尝试使用 CONTEXT_INFO

尝试SET CONTEXT_INFO 'A Job'在你的工作中添加一个 T-SQL 步骤

然后尝试在触发器中使用sys.dm_exec_sessions

我很好奇它是否有效 - 请发布您的发现。

http://msdn.microsoft.com/en-us/library/ms187768(v=sql.105).aspx

于 2014-05-15T23:22:35.117 回答