Получить работу, которая запустила SQL-запрос по триггеру UPDATE

Я пытаюсь создать контрольный журнал для действий, выполняемых в веб-приложении, заданиях агента сервера SQL и вручную выполнять запросы к базе данных. Я пытаюсь использовать триггеры, чтобы ловить обновления, вставки и удаления в определенных таблицах.

В целом этот процесс работает. Например, пользователь выполняет обновление в веб-приложении, и триггер записывает обновленные данные в определенную мной таблицу контрольного журнала, включая имя пользователя, который выполнил действие. Это прекрасно работает с точки зрения веб-приложения или запроса вручную, но у нас также есть десятки заданий агента 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: Я просто хочу уточнить, я ищу решение, которое не требует, чтобы я пошел на каждую работу и что-то изменить. Слишком много рабочих мест, чтобы сделать это возможным решением.

3 ответа

Решение

РАБОЧИЙ КОД В ПЕРВОМ РЕДАКЦИИ - (еще одинкряк)

Использовать app_name() Функция http://msdn.microsoft.com/en-us/library/ms189770.aspx в триггере аудита, чтобы получить имя приложения, выполняющего запрос.

Для заданий агента SQL имя_приложения содержит идентификатор шага задания в имени приложения (если это шаг T-SQL). Мы делаем это в наших триггерах аудита и отлично работает. Примером app_name() результаты при запуске из триггера аудита:

SQL Agent - TSQL JobStep (задание 0x96EB56A24786964889AB504D9A920D30: шаг 1)

Эту работу можно посмотреть через job_id колонка в msdb.dbo.sysjobs_view,

Поскольку пакеты служб SSIS инициируют подключение SQL вне механизма заданий агента SQL, эти подключения будут иметь свое собственное имя приложения, и вам необходимо указать имя приложения в строках подключения пакетов служб SSIS. В пакетах служб SSIS, веб-приложениях, WinForms или любом клиенте, который подключается к SQL Server, вы можете установить значение, возвращаемое функцией app_name, используя это в строке подключения:

"Application Name=MyAppNameGoesHere;" 

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

Если "Имя приложения" не задано в строке подключения.NET, тогда значение по умолчанию при использовании System.Data.SqlClient.SqlConnection такое "поставщик данных.Net SqlClient".

Некоторые другие поля, которые обычно используются для аудита:

  • HOST_NAME (): http://technet.microsoft.com/en-us/library/ms178598.aspx Возвращает имя подключаемого клиентского компьютера. Это полезно, если у вас есть приложение для интранета.
  • CONNECTIONPROPERTY ('local_net_address'): для получения IP-адреса клиента.
  • CONTEXT_INFO (): http://technet.microsoft.com/en-us/library/ms187768.aspx Вы можете использовать это для хранения информации в течение всего времени соединения / сеанса. Context_Info - это двоичное 128-байтовое поле, поэтому вам может потребоваться выполнить преобразование в / из строк при его использовании.

Вот вспомогательные методы 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 столы. Возможно, вам придется настроить GRANT для SELECT в базе данных msdb для этих таблиц, чтобы запрос был выполнен успешно, или создать представление с использованием этого запроса и настроить разрешения для этого представления.

Запрос:

;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%28v=sql.110%29.aspx чтобы получить список запущенных в настоящее время пакетов служб SSIS, независимо от того, были ли они запущены из запланированного задания. Я не видел аналогичного представления в версиях SQL Server до 2012 года.

Я хотел бы добавить дополнительный столбец к вашей таблице, например Update_Source, и получить все исходные приложения (включая SSIS), чтобы установить его при обновлении таблицы.

Вы можете использовать ПОЛЬЗОВАТЕЛЬ в качестве ПО УМОЛЧАНИЮ для этого столбца, чтобы минимизировать необходимые изменения.

Вы можете попробовать использовать CONTEXT_INFO

Попробуйте добавить шаг T-SQL с SET CONTEXT_INFO 'A Job' в вашей работе

Затем попробуйте прочитать это в вашем триггере, используя sys.dm_exec_sessions

Мне любопытно посмотреть, работает ли это - пожалуйста, опубликуйте свои выводы.

http://msdn.microsoft.com/en-us/library/ms187768%28v=sql.105%29.aspx

Другие вопросы по тегам