sql - 获取在 UPDATE 触发器上运行 SQL 查询的作业

标签 sql sql-server triggers ssis sql-server-job

我正在尝试为在 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

我运行了一个特定的作业来测试它似乎工作,因为任何正在运行的其他作业都将列在 @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:我只是想澄清一下,我正在寻找一种解决方案,不需要我参与每项工作并做出任何改变。有太多的工作使这成为一个可行的解决方案。

最佳答案

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

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

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

SQLAgent - TSQL JobStep (Job 0x96EB56A24786964889AB504D9A920D30 : Step 1)

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

由于 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 Data Provider”。

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

以下是用于设置/获取上下文信息的 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,以便查询成功,或者使用此查询创建 View ,然后调整该 View 的授权。

查询:

;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 View http://msdn.microsoft.com/en-us/library/ff878089(v=sql.110).aspx获取当前正在运行的 SSIS 包的列表,无论它们是否是从计划作业中启动的。我没有在 2012 年之前的 SQL Server 版本中看到等效 View 。

关于sql - 获取在 UPDATE 触发器上运行 SQL 查询的作业,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23655913/

相关文章:

sql - 执行 SQL 错误,因为里面有单引号

java - Hibernate Generated Value 策略

sql - 如何使用 TSQL 更新 SQL SERVER 中的 BLOB

sql - PL/SQL 触发器问题

MySQL如何对表的最低值(value)/价格进行排序,并且只显示最低的名称?

sql - 微软 SQL : is it possible to solve this task in SQL without T/PL sql?

sql - 当本地数据库上没有 -S 参数时,SQL 命令中的 BCP 给出 NativeError = 2

arrays - 错误 : ARRAY is missing type definition for its values Sequelize

c# - 在运行时动态修改样式(添加触发器)

java - Hibernate映射与DB触发器+序列生成的ID