Время прочтения: 4 мин.

Существуют различные способы идентификации запросов на сервере: системные процедуры sp_who и sp_who2, Activity Monitor, Query Store.

Но в этом материале речь пойдёт о хранимой процедуре sp_WhoIsActive. Она не является разработкой Microsoft, а создана программистом Адамом Мачаником и распространяется бесплатно.

В моей работе возникла острая необходимость логирования текущей активности на сервере. Выбор был сделан в пользу sp_WhoIsActive по причине сочетания в ней приемлемой ресурсоёмкости и достаточной полноты получаемых данных. Для этого приведу пример реализации джоба, вызывающего системную процедуру по расписанию.

Итак, скрипт, с помощью которого можно запустить процедуру с созданием соответствующих таблиц в случае их отсутствия:

SET NOCOUNT ON;

DECLARE
        --логирующая таблица
        @dst_tbl varchar(500) = 'MySchema.WhoIsActive',
	 --временная таблица, куда мы будем писать результаты работы WhoIsActive
	 @dst_tmp_tbl varchar(500) = '##WhoIsActiveTmp',
	 --база данных, где будет лежать логирующая таблица;
        @dst_db varchar(100) = 'DestDb',
	 --база данных, запросы к которой нужно логировать
	 @log_db varchar(100)= 'MyDb',
	 --переменная, содержащая SQL-запрос
        @SQL nvarchar(4000),
	 --параметры
        @params nvarchar(500),
	 --флаг существования объекта
        @exists bit;

SET @dst_tbl = @dst_db + '.' + @dst_tbl;
 
--проверим существует ли логирующая таблица с соответствующим названием
IF OBJECT_ID(@dst_tbl) IS NULL
BEGIN;
--получим с помощью параметра @schema информацию о возвращаемых значениях sp_WhoIsActive
        EXEC dbo.sp_WhoIsActive  @get_transaction_info = 1, @get_outer_command = 1,
                        @get_plans = 1, @return_schema = 1, @schema = @SQL OUTPUT;
	 --инициализация запроса для создания логирующей таблицы
        SET @SQL = REPLACE (@SQL, '<table_name>', @dst_tbl);
	 --CREATE TABLE
        EXEC (@SQL);

END;

-- создадим временную таблицу для того, чтобы поместить туда результат WhoIsActive для последующей фильтрации
--проверяем наличие временной таблицы
IF OBJECT_ID ('tempdb..'+@dst_tmp_tbl) IS NULL
BEGIN;
	 --аналогично запускаем CREATE TABLE для временной таблицы
 EXEC dbo.sp_WhoIsActive  @get_transaction_info = 1, @get_outer_command = 1,
                        @get_plans = 1, @return_schema = 1, @schema = @SQL OUTPUT;
        SET @SQL = REPLACE(@SQL, '<table_name>', @dst_tmp_tbl);
        EXEC (@SQL);
END;

--проверим наличие кластерного индекса на таблице
SET @SQL
      = 'USE ' + @dst_db +  ' IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(@dst_tbl) AND name = N''PK_WhoIsActive'') SET @exists = 0';
SET @params = N'@dst_tbl varchar(500), @exists bit OUTPUT';
EXEC sys.sp_executesql @SQL, @params, @dst_tbl = @dst_tbl, @exists = @exists OUTPUT;

--чтобы поиск по логирующей таблице был пошустрей, создадим на ней кластерный индекс
IF @exists = 0
    BEGIN;
        SET @SQL = 'CREATE CLUSTERED INDEX PK_WhoIsActive ON ' + @dst_tbl + '(collection_time ASC)';
        EXEC (@SQL);
    END;

--запускаем скрипт, записываем результат во временную таблицу
EXEC dbo.sp_WhoIsActive @get_transaction_info = 1,
                        @get_outer_command = 1,
                        @get_plans = 1,
                        @destination_table= @dst_tmp_tbl;

--переместим из временной таблицы в логирующую
SET @SQL = '						
	INSERT INTO ' + @dst_tbl + 
	+ ' SELECT *
		FROM ' + @dst_tmp_tbl + '
		WHERE [database_name] = ' +  QUOTENAME(@log_db, '''');
EXEC (@SQL)

EXEC ('TRUNCATE TABLE ' + @dst_tmp_tbl);

Далее в Агент SQL Server, создаём задание, добавляем скрипт и ставим на расписание, допустим, каждые 15 минут:

После этого вставляем скрипт в шаг задания:

<

В результате мы получили инструмент, который каждые 15 минут логирует текущую активность в базе данных. Конечно, срез в 15 минут – это слишком большой интервал времени. Гораздо уместнее запускать процедуру каждые, например, 30 секунд, так как, порой, выполнение запроса укладывается в считанные секунды. И всё же, длительные запросы, которые часто представляют гораздо больший интерес из-за своей “тяжести”, эдакие киты среди запросов, мы без проблем обнаружим.