SQL Server: как найти, какие строки выполняются - PullRequest
0 голосов
/ 30 июня 2019

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

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

С помощью такого запроса я могу видеть, какие хранимые процедуры / триггеры / функции выполняются, так как язнаю, когда я вызываю хранимую процедуру, я могу использовать время, чтобы увидеть, была ли она выполнена.

SELECT  d.object_id, d.database_id, 
         OBJECT_NAME(object_id, database_id) AS proc_name, 
         MAX( d.last_execution_time) as last_execution_time,
         OBJECT_DEFINITION(object_id) as definition
         FROM sys.dm_exec_procedure_stats AS d 
         WHERE d.database_id = DB_ID()
         GROUP BY  d.object_id, d.database_id, 
         OBJECT_NAME(object_id, database_id) 

Как мне найти строки / операторы, которые были выполнены, я также должен знать внутри какой хранимой процедуры/ trigger / function строки / операторы существуют и в какой это форме.Я должен учитывать, что можно использовать оператор IF / ELSE.

С этими данными я могу сделать 2 важные вещи:

  • создать отчет о покрытии кода
  • Оптимизировать, какие строки нужно изменить, поскольку мне не нужно мутировать непокрытые строки.

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

Обратите внимание, что я не могу изменить код, который пользователи хотят тестировать с моей платформой.Я могу искать шаблоны и заменять их, но ручное изменение процедур НЕ является опцией.

РЕДАКТИРОВАТЬ: позволяет переформулировать этот вопрос: как разделить определение хранимой процедуры на различные операторы таким образом, чтобы это не зависело от стиля кода?и Как добавить новый оператор между найденными операторами?

РЕДАКТИРОВАТЬ: в сообщении SO SQL Server: Как разобрать код в его различные операторы Я нашел способ отследить выполнение операторов, но я пока не могу его отфильтровать.

Ответы [ 2 ]

0 голосов
/ 09 июля 2019

Таким образом, расширенные события являются решением, вот как я это сделал:

IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='testMSSQLTrace')  
   DROP EVENT SESSION testMSSQLTrace ON SERVER;  

DECLARE @cmd VARCHAR(MAX) = '';
SELECT @cmd = 'CREATE EVENT SESSION testMSSQLTrace 
ON SERVER
    ADD EVENT sqlserver.sp_statement_completed
        (WHERE (sqlserver.database_name = N''' + DB_NAME() + '''))
    ADD TARGET package0.ring_buffer
        WITH (
            MAX_MEMORY = 2048 KB,
            EVENT_RETENTION_MODE = NO_EVENT_LOSS,
            MAX_DISPATCH_LATENCY = 3 SECONDS,
            MAX_EVENT_SIZE = 0 KB,
            MEMORY_PARTITION_MODE = NONE,
            TRACK_CAUSALITY = OFF,
            STARTUP_STATE = OFF
        );'

EXEC (@cmd)

Это создает событие, которое может быть запущено после каждого завершения оператора, это делается динамически для фильтрации в базе данных.

Затем у меня есть 3 процедуры, которые облегчают управление этим событием

/*******************************************************************************************
    Starts the statement trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_StartTrace
AS
BEGIN 
    ALTER EVENT SESSION testMSSQLTrace
          ON SERVER
        STATE = START; 
END
GO

/*******************************************************************************************
    Ends the statement trace, this also clears the trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_StopTrace
AS
BEGIN
    ALTER EVENT SESSION testMSSQLTrace
          ON SERVER
        STATE = STOP; 
END
GO


/*******************************************************************************************
    Saves the statements trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_SaveTrace
AS
BEGIN
    DECLARE @xml XML;

    SELECT @xml = CAST(xet.target_data AS xml)
        FROM sys.dm_xe_session_targets AS xet INNER JOIN sys.dm_xe_sessions AS xe ON (xe.address = xet.event_session_address)  
        WHERE xe.name = 'testMSSQLTrace'  

    INSERT INTO testMSSQL.StatementInvocations (testProcedure, procedureName, lineNumber, statement)
        SELECT testMSSQL.GetCurrentTest(), 
            OBJECT_NAME(T.c.value('(data[@name="object_id"]/value)[1]', 'int')),
            T.c.value('(data[@name="line_number"]/value)[1]', 'int'), 
            T.c.value('(data[@name="statement"]/value)[1]', 'VARCHAR(900)')
        FROM @xml.nodes('RingBufferTarget/event') T(c)
        WHERE T.c.value('(data[@name="nest_level"]/value)[1]', 'int') > 3

END
GO

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

Наконец, я использую его примерно так:

start trace
start tran/savepoint
run SetUp (users code)
run test (users code)
save trace
save trace to variable
rollback tran (also catch errors and stuff like that)
save variable back to table so the trace is not rolled back

Особая благодарность @Jeroen Mosterd за то, что он изначально предложил предложение для этого решения вthis SQL Server: как разобрать код в различные операторы SO post

0 голосов
/ 30 июня 2019

Вы можете либо:

  • Добавить параметр @ DEBUG к каждой хранимой процедуре, которую вы вызываете, либо
  • Записывать все, что вы хотите, либо
  • Регистрируйте только тогда, когда вам нужно.

С помощью параметра @Debug вы можете по умолчанию установить его на OFF, а затем вызывать его с помощью ON, если вы хотите отследить ваши операторы, с помощью следующего кода:

IF (@Debug = 1) PRINT 'your tracing information goes here';

Если вы хотите регистрировать все, создайте таблицу журнала и вставьте в нее строку, где вам нужно знать, какой оператор был выполнен, например:

DECLARE @log AS TABLE (msg VARCHAR(MAX));

и

INSERT INTO @log VALUES('your tracing information goes here');

Или вы можете объединить их:

IF (@Debug = 1) INSERT INTO @log VALUES('your tracing information goes here');

Конечно, это повлияет на производительность, даже если вы не выводите / не регистрируете.

...