我正在尝试为在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, '')
当我运行作业时,在作业运行时运行上述查询,返回正确的作业。但是,当通过SQL Server代理作业运行SSIS包或在SSIS中手动运行时,@runningJobs
不会填充,只返回NULL
。
所以我现在认为这是SSIS和master.dbo.xp_sqlagent_enum_jobs
权限的问题。还有其他想法吗?
编辑#2 :实际上不要认为这是权限错误。此代码下面有一个INSERT
语句,如果它是权限错误,则INSERT
语句不会运行,因此审计行不会添加到数据库中。因此,由于在数据库中添加了一行,而不是填充了runningJobs
字段。奇怪的时候。
编辑#3 :我只想澄清,我正在寻找一个解决方案,不要求我进入每项工作并改变任何事情。有太多的工作要做,这是一个可行的解决方案。
答案 0 :(得分: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 Agent作业引擎之外启动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数据库中为这些表调整GRANTs for SELECT以使查询成功,或者使用此查询创建视图,并调整该视图的授权。
查询:
;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版本中看到过等效的视图。
答案 1 :(得分:0)
我会在您的表格中添加一个额外的列,例如Update_Source,并获取所有源应用程序(包括SSIS)以在更新表时进行设置。
您可以将USER用作该列的DEFAULT,以最大限度地减少所需的更改。
答案 2 :(得分: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