Как просмотреть историю запросов в SQL Server Management Studio


159

Сохраняется ли история запросов в некоторых файлах журнала? Если да, можете ли вы сказать мне, как найти их местоположение? Если нет, можете ли вы дать мне совет, как это увидеть?


1
http://www.ssmstoolspack.com/ предоставляет окно истории, если это то, что вам нужно.
TI

Ответы:


226

[Поскольку этот вопрос , скорее всего, будет закрыт как дубликат.]

Если SQL Server не был перезапущен (и план не был удален и т. Д.), Вы можете найти запрос в кэше плана.

SELECT t.[text]
FROM sys.dm_exec_cached_plans AS p
CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t
WHERE t.[text] LIKE N'%something unique about your query%';

Если вы потеряли файл из-за сбоя Management Studio, вы можете найти файлы восстановления здесь:

C:\Users\<you>\Documents\SQL Server Management Studio\Backup Files\

В противном случае вам нужно будет использовать что-то еще, чтобы помочь вам сохранить историю запросов, например, SSMS Tools Pack, как упомянуто в ответе Эда Харпера, хотя это не бесплатно в SQL Server 2012+. Или вы можете настроить легкую трассировку, отфильтрованную по вашему логину или имени хоста (но для этого используйте трассировку на стороне сервера, а не Profiler).


Как прокомментировал @ Nenad-Zivkovic, было бы полезно присоединиться sys.dm_exec_query_statsи заказать last_execution_time:

SELECT t.[text], s.last_execution_time
FROM sys.dm_exec_cached_plans AS p
INNER JOIN sys.dm_exec_query_stats AS s
   ON p.plan_handle = s.plan_handle
CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t
WHERE t.[text] LIKE N'%something unique about your query%'
ORDER BY s.last_execution_time DESC;

9
Также может помочь присоединиться sys.dm_exec_query_statsи искать или заказать поlast_execution_time
Nenad Zivkovic

Не работает с SQL Server 2000 и работает с SQL Server 2005
Durai Amuthan.H

@Duraiamuthan Хорошо, вопрос задается о Management Studio, поэтому предположить, что 2005+ будет безопасно. В 2000 году не было Management Studio, в нем был Query Analyzer. 2000 также много лет без поддержки. Если вы хотите решить эту проблему для SQL Server 2000, вам, вероятно, следует задать новый вопрос, помеченный этой конкретной версией (если дубликат не существует, который я не проверял).
Аарон Бертран

1
@AaronBertrand Мой комментарий дополняет ваш ответ. Он поможет другим
Durai Amuthan.H

3
@AaronBertrand Ты бог среди людей.
AnotherDeveloper

49

В конце, но, надеюсь, полезно, поскольку он добавляет больше деталей ...

По умолчанию невозможно просмотреть запросы, выполненные в SSMS. Есть несколько вариантов, хотя.

Чтение журнала транзакций - это не простая вещь, потому что она в проприетарном формате. Однако, если вам нужно увидеть запросы, которые выполнялись исторически (кроме SELECT), это единственный способ.

Для этого вы можете использовать сторонние инструменты, такие как ApexSQL Log и SQL Log Rescue (бесплатно, но только для SQL 2000). Проверьте эту тему для получения более подробной информации здесь SQL Server Transaction Log Explorer / Analyzer

Профилировщик SQL Server - лучше всего подходит, если вы просто хотите начать аудит и вас не интересует, что произошло раньше. Убедитесь, что вы используете фильтры, чтобы выбрать только те транзакции, которые вам нужны. В противном случае вы получите очень много данных очень быстро.

Трассировка SQL Server - лучше всего подходит, если вы хотите захватить все или большинство команд и сохранить их в файле трассировки, который может быть проанализирован позже.

Триггеры - лучше всего подходят, если вы хотите захватить DML (кроме select) и сохранить их где-нибудь в базе данных


Создание файла трассировки в профилировщике SQL Server ( msdn.microsoft.com/en-us/library/ms175047(v=sql.110).aspx ) с использованием стандартного шаблона - это хороший способ перейти к мониторингу выполненных операторов.
javiniar.leonard


6

Как уже отмечали другие, вы можете использовать SQL Profiler, но вы также можете использовать его функциональность с помощью системных хранимых процедур sp_trace_ *. Например, этот фрагмент кода SQL (по крайней мере, в 2000 году; я думаю, что то же самое для SQL 2008, но вам придется перепроверить) поймаетRPC:Completed и SQL:BatchCompletedсобытия для всех запросов, выполнение которых занимает более 10 секунд, и сохранять выходные данные в файл трассировки, который вы можете открыть в профилировщике SQL позже:

DECLARE @TraceID INT
DECLARE @ON BIT
DECLARE @RetVal INT
SET @ON = 1

exec @RetVal = sp_trace_create @TraceID OUTPUT, 2, N'Y:\TraceFile.trc'
print 'This trace is Trace ID = ' + CAST(@TraceID AS NVARCHAR)
print 'Return value = ' + CAST(@RetVal AS NVARCHAR)
-- 10 = RPC:Completed
exec sp_trace_setevent @TraceID, 10, 1, @ON     -- Textdata
exec sp_trace_setevent @TraceID, 10, 3, @ON     -- DatabaseID
exec sp_trace_setevent @TraceID, 10, 12, @ON        -- SPID
exec sp_trace_setevent @TraceID, 10, 13, @ON        -- Duration
exec sp_trace_setevent @TraceID, 10, 14, @ON        -- StartTime
exec sp_trace_setevent @TraceID, 10, 15, @ON        -- EndTime

-- 12 = SQL:BatchCompleted
exec sp_trace_setevent @TraceID, 12, 1, @ON     -- Textdata
exec sp_trace_setevent @TraceID, 12, 3, @ON     -- DatabaseID
exec sp_trace_setevent @TraceID, 12, 12, @ON        -- SPID
exec sp_trace_setevent @TraceID, 12, 13, @ON        -- Duration
exec sp_trace_setevent @TraceID, 12, 14, @ON        -- StartTime
exec sp_trace_setevent @TraceID, 12, 15, @ON        -- EndTime

-- Filter for duration [column 13] greater than [operation 2] 10 seconds (= 10,000ms)
declare @duration bigint
set @duration = 10000
exec sp_trace_setfilter @TraceID, 13, 0, 2, @duration

Вы можете найти идентификатор для каждого события трассировки, столбцов и т. Д. В Books Online; просто найдите sp_trace_create , sp_trace_setevent и sp_trace_setfiler sprocs. Затем вы можете контролировать трассировку следующим образом:

exec sp_trace_setstatus 15, 0       -- Stop the trace
exec sp_trace_setstatus 15, 1       -- Start the trace
exec sp_trace_setstatus 15, 2       -- Close the trace file and delete the trace settings

... где '15' - это идентификатор трассировки (как сообщается в sp_trace_create, который первый скрипт выкинул выше).

Вы можете проверить, какие трассы выполняются:

select * from ::fn_trace_getinfo(default)

Единственное, что я скажу с осторожностью - я не знаю, какую нагрузку это окажет на вашу систему; это добавит некоторые, но насколько велико это «некоторые», вероятно, зависит от того, насколько занят ваш сервер.


Полезный код. Это сработало только для меня, когда я удалил расширение .trc.
Стив Смит

5

Система не записывает запросы таким образом. Если вы знаете, что хотите сделать это заранее, вы можете использовать SQL Profiler для записи поступающих сообщений и отслеживания запросов во время работы Profiler.


5

Я использую приведенный ниже запрос для отслеживания активности приложения на сервере SQL, на котором не включен профилировщик трассировки. Метод использует хранилище запросов (SQL Server 2016+) вместо DMV. Это дает лучшую возможность просмотра исторических данных, а также ускоряет поиск. Очень эффективно захватывать краткосрочные запросы, которые не могут быть захвачены sp_who / sp_whoisactive.

/* Adjust script to your needs.
    Run full script (F5) -> Interact with UI -> Run full script again (F5)
    Output will contain the queries completed in that timeframe.
*/

/* Requires Query Store to be enabled:
    ALTER DATABASE <db> SET QUERY_STORE = ON
    ALTER DATABASE <db> SET QUERY_STORE (OPERATION_MODE = READ_WRITE, MAX_STORAGE_SIZE_MB = 100000)
*/

USE <db> /* Select your DB */

IF OBJECT_ID('tempdb..#lastendtime') IS NULL
    SELECT GETUTCDATE() AS dt INTO #lastendtime
ELSE IF NOT EXISTS (SELECT * FROM #lastendtime)
    INSERT INTO #lastendtime VALUES (GETUTCDATE()) 

;WITH T AS (
SELECT 
    DB_NAME() AS DBName
    , s.name + '.' + o.name AS ObjectName
    , qt.query_sql_text
    , rs.runtime_stats_id
    , p.query_id
    , p.plan_id
    , CAST(p.last_execution_time AS DATETIME) AS last_execution_time
    , CASE WHEN p.last_execution_time > #lastendtime.dt THEN 'X' ELSE '' END AS New
    , CAST(rs.last_duration / 1.0e6 AS DECIMAL(9,3)) last_duration_s
    , rs.count_executions
    , rs.last_rowcount
    , rs.last_logical_io_reads
    , rs.last_physical_io_reads
    , q.query_parameterization_type_desc
FROM (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY plan_id, runtime_stats_id ORDER BY runtime_stats_id DESC) AS recent_stats_in_current_priod
    FROM sys.query_store_runtime_stats 
    ) AS rs
INNER JOIN sys.query_store_runtime_stats_interval AS rsi ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
INNER JOIN sys.query_store_plan AS p ON p.plan_id = rs.plan_id
INNER JOIN sys.query_store_query AS q ON q.query_id = p.query_id
INNER JOIN sys.query_store_query_text AS qt ON qt.query_text_id = q.query_text_id
LEFT OUTER JOIN sys.objects AS o ON o.object_id = q.object_id
LEFT OUTER JOIN sys.schemas AS s ON s.schema_id = o.schema_id
CROSS APPLY #lastendtime
WHERE rsi.start_time <= GETUTCDATE() AND GETUTCDATE() < rsi.end_time
    AND recent_stats_in_current_priod = 1
    /* Adjust your filters: */
    -- AND (s.name IN ('<myschema>') OR s.name IS NULL)
UNION
SELECT NULL,NULL,NULL,NULL,NULL,NULL,dt,NULL,NULL,NULL,NULL,NULL,NULL, NULL
FROM #lastendtime
)
SELECT * FROM T
WHERE T.query_sql_text IS NULL OR T.query_sql_text NOT LIKE '%#lastendtime%' -- do not show myself
ORDER BY last_execution_time DESC

TRUNCATE TABLE #lastendtime
INSERT INTO #lastendtime VALUES (GETUTCDATE()) 

4
SELECT deqs.last_execution_time AS [Time], dest.text AS [Query], dest.*
FROM sys.dm_exec_query_stats AS deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest
WHERE dest.dbid = DB_ID('msdb')
ORDER BY deqs.last_execution_time DESC

Это должно показать вам время и дату, когда был выполнен запрос



3

Историю запросов можно посмотреть с помощью системных представлений:

  1. sys.dm_exec_query_stats
  2. sys.dm_exec_sql_text
  3. sys.dm_exec_query_plan

Например, используя следующий запрос:

select  top(100)
        creation_time,
        last_execution_time,
        execution_count,
        total_worker_time/1000 as CPU,
        convert(money, (total_worker_time))/(execution_count*1000)as [AvgCPUTime],
        qs.total_elapsed_time/1000 as TotDuration,
        convert(money, (qs.total_elapsed_time))/(execution_count*1000)as [AvgDur],
        total_logical_reads as [Reads],
        total_logical_writes as [Writes],
        total_logical_reads+total_logical_writes as [AggIO],
        convert(money, (total_logical_reads+total_logical_writes)/(execution_count + 0.0)) as [AvgIO],
        [sql_handle],
        plan_handle,
        statement_start_offset,
        statement_end_offset,
        plan_generation_num,
        total_physical_reads,
        convert(money, total_physical_reads/(execution_count + 0.0)) as [AvgIOPhysicalReads],
        convert(money, total_logical_reads/(execution_count + 0.0)) as [AvgIOLogicalReads],
        convert(money, total_logical_writes/(execution_count + 0.0)) as [AvgIOLogicalWrites],
        query_hash,
        query_plan_hash,
        total_rows,
        convert(money, total_rows/(execution_count + 0.0)) as [AvgRows],
        total_dop,
        convert(money, total_dop/(execution_count + 0.0)) as [AvgDop],
        total_grant_kb,
        convert(money, total_grant_kb/(execution_count + 0.0)) as [AvgGrantKb],
        total_used_grant_kb,
        convert(money, total_used_grant_kb/(execution_count + 0.0)) as [AvgUsedGrantKb],
        total_ideal_grant_kb,
        convert(money, total_ideal_grant_kb/(execution_count + 0.0)) as [AvgIdealGrantKb],
        total_reserved_threads,
        convert(money, total_reserved_threads/(execution_count + 0.0)) as [AvgReservedThreads],
        total_used_threads,
        convert(money, total_used_threads/(execution_count + 0.0)) as [AvgUsedThreads],
        case 
            when sql_handle IS NULL then ' '
            else(substring(st.text,(qs.statement_start_offset+2)/2,(
                case
                    when qs.statement_end_offset =-1 then len(convert(nvarchar(MAX),st.text))*2      
                    else qs.statement_end_offset    
                end - qs.statement_start_offset)/2  ))
        end as query_text,
        db_name(st.dbid) as database_name,
        object_schema_name(st.objectid, st.dbid)+'.'+object_name(st.objectid, st.dbid) as [object_name],
        sp.[query_plan]
from sys.dm_exec_query_stats as qs with(readuncommitted)
cross apply sys.dm_exec_sql_text(qs.[sql_handle]) as st
cross apply sys.dm_exec_query_plan(qs.[plan_handle]) as sp
WHERE st.[text] LIKE '%query%'

Текущие запущенные запросы можно увидеть с помощью следующего скрипта:

select ES.[session_id]
      ,ER.[blocking_session_id]
      ,ER.[request_id]
      ,ER.[start_time]
      ,DateDiff(second, ER.[start_time], GetDate()) as [date_diffSec]
      , COALESCE(
                    CAST(NULLIF(ER.[total_elapsed_time] / 1000, 0) as BIGINT)
                   ,CASE WHEN (ES.[status] <> 'running' and isnull(ER.[status], '')  <> 'running') 
                            THEN  DATEDIFF(ss,0,getdate() - nullif(ES.[last_request_end_time], '1900-01-01T00:00:00.000'))
                    END
                ) as [total_time, sec]
      , CAST(NULLIF((CAST(ER.[total_elapsed_time] as BIGINT) - CAST(ER.[wait_time] AS BIGINT)) / 1000, 0 ) as bigint) as [work_time, sec]
      , CASE WHEN (ER.[status] <> 'running' AND ISNULL(ER.[status],'') <> 'running') 
                THEN  DATEDIFF(ss,0,getdate() - nullif(ES.[last_request_end_time], '1900-01-01T00:00:00.000'))
        END as [sleep_time, sec] --Время сна в сек
      , NULLIF( CAST((ER.[logical_reads] + ER.[writes]) * 8 / 1024 as numeric(38,2)), 0) as [IO, MB]
      , CASE  ER.transaction_isolation_level
        WHEN 0 THEN 'Unspecified'
        WHEN 1 THEN 'ReadUncommited'
        WHEN 2 THEN 'ReadCommited'
        WHEN 3 THEN 'Repetable'
        WHEN 4 THEN 'Serializable'
        WHEN 5 THEN 'Snapshot'
        END as [transaction_isolation_level_desc]
      ,ER.[status]
      ,ES.[status] as [status_session]
      ,ER.[command]
      ,ER.[percent_complete]
      ,DB_Name(coalesce(ER.[database_id], ES.[database_id])) as [DBName]
      , SUBSTRING(
                    (select top(1) [text] from sys.dm_exec_sql_text(ER.[sql_handle]))
                  , ER.[statement_start_offset]/2+1
                  , (
                        CASE WHEN ((ER.[statement_start_offset]<0) OR (ER.[statement_end_offset]<0))
                                THEN DATALENGTH ((select top(1) [text] from sys.dm_exec_sql_text(ER.[sql_handle])))
                             ELSE ER.[statement_end_offset]
                        END
                        - ER.[statement_start_offset]
                    )/2 +1
                 ) as [CURRENT_REQUEST]
      ,(select top(1) [text] from sys.dm_exec_sql_text(ER.[sql_handle])) as [TSQL]
      ,(select top(1) [objectid] from sys.dm_exec_sql_text(ER.[sql_handle])) as [objectid]
      ,(select top(1) [query_plan] from sys.dm_exec_query_plan(ER.[plan_handle])) as [QueryPlan]
      ,NULL as [event_info]--(select top(1) [event_info] from sys.dm_exec_input_buffer(ES.[session_id], ER.[request_id])) as [event_info]
      ,ER.[wait_type]
      ,ES.[login_time]
      ,ES.[host_name]
      ,ES.[program_name]
      ,cast(ER.[wait_time]/1000 as decimal(18,3)) as [wait_timeSec]
      ,ER.[wait_time]
      ,ER.[last_wait_type]
      ,ER.[wait_resource]
      ,ER.[open_transaction_count]
      ,ER.[open_resultset_count]
      ,ER.[transaction_id]
      ,ER.[context_info]
      ,ER.[estimated_completion_time]
      ,ER.[cpu_time]
      ,ER.[total_elapsed_time]
      ,ER.[scheduler_id]
      ,ER.[task_address]
      ,ER.[reads]
      ,ER.[writes]
      ,ER.[logical_reads]
      ,ER.[text_size]
      ,ER.[language]
      ,ER.[date_format]
      ,ER.[date_first]
      ,ER.[quoted_identifier]
      ,ER.[arithabort]
      ,ER.[ansi_null_dflt_on]
      ,ER.[ansi_defaults]
      ,ER.[ansi_warnings]
      ,ER.[ansi_padding]
      ,ER.[ansi_nulls]
      ,ER.[concat_null_yields_null]
      ,ER.[transaction_isolation_level]
      ,ER.[lock_timeout]
      ,ER.[deadlock_priority]
      ,ER.[row_count]
      ,ER.[prev_error]
      ,ER.[nest_level]
      ,ER.[granted_query_memory]
      ,ER.[executing_managed_code]
      ,ER.[group_id]
      ,ER.[query_hash]
      ,ER.[query_plan_hash]
      ,EC.[most_recent_session_id]
      ,EC.[connect_time]
      ,EC.[net_transport]
      ,EC.[protocol_type]
      ,EC.[protocol_version]
      ,EC.[endpoint_id]
      ,EC.[encrypt_option]
      ,EC.[auth_scheme]
      ,EC.[node_affinity]
      ,EC.[num_reads]
      ,EC.[num_writes]
      ,EC.[last_read]
      ,EC.[last_write]
      ,EC.[net_packet_size]
      ,EC.[client_net_address]
      ,EC.[client_tcp_port]
      ,EC.[local_net_address]
      ,EC.[local_tcp_port]
      ,EC.[parent_connection_id]
      ,EC.[most_recent_sql_handle]
      ,ES.[host_process_id]
      ,ES.[client_version]
      ,ES.[client_interface_name]
      ,ES.[security_id]
      ,ES.[login_name]
      ,ES.[nt_domain]
      ,ES.[nt_user_name]
      ,ES.[memory_usage]
      ,ES.[total_scheduled_time]
      ,ES.[last_request_start_time]
      ,ES.[last_request_end_time]
      ,ES.[is_user_process]
      ,ES.[original_security_id]
      ,ES.[original_login_name]
      ,ES.[last_successful_logon]
      ,ES.[last_unsuccessful_logon]
      ,ES.[unsuccessful_logons]
      ,ES.[authenticating_database_id]
      ,ER.[sql_handle]
      ,ER.[statement_start_offset]
      ,ER.[statement_end_offset]
      ,ER.[plan_handle]
      ,NULL as [dop]--ER.[dop]
      ,coalesce(ER.[database_id], ES.[database_id]) as [database_id]
      ,ER.[user_id]
      ,ER.[connection_id]
from sys.dm_exec_requests ER with(readuncommitted)
right join sys.dm_exec_sessions ES with(readuncommitted)
on ES.session_id = ER.session_id 
left join sys.dm_exec_connections EC  with(readuncommitted)
on EC.session_id = ES.session_id
where ER.[status] in ('suspended', 'running', 'runnable')
or exists (select top(1) 1 from sys.dm_exec_requests as ER0 where ER0.[blocking_session_id]=ES.[session_id])

Этот запрос отображает все активные запросы и все те запросы, которые явно блокируют активные запросы.

Все эти и другие полезные скрипты реализованы в виде представлений в базе данных SRV , которая распространяется свободно. Например, первый скрипт появился из представления [inf]. [VBigQuery] , а второй - из представления [inf]. [VRequests] .

Существуют также различные сторонние решения для истории запросов. Я использую Query Manager из Dbeaver : введите описание изображения здесь и Query Execution History из SQL Tools , который встроен в SSMS : введите описание изображения здесь


1

Эта функция не существует "из коробки" в SSMS.

Если вы используете SSMS 18 или новее, вы можете попробовать SSMSPlus.

Он имеет функцию истории запросов.

https://github.com/akarzazi/SSMSPlus

Отказ от ответственности: я автор.


0

Вы можете использовать «Автоматически генерировать скрипт при каждом сохранении», если вы используете Management Studio. Это не обязательно логирование. Проверьте, полезно ли вам ..;)


0

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


0

Немного нестандартный метод заключается в написании сценария решения в AutoHotKey. Я использую это, и это не идеально, но работает и бесплатно. По сути, этот сценарий назначает горячую клавишу CTRL+ SHIFT+, Rкоторая скопирует выбранный SQL в SSMS ( CTRL+ C), сохранит файл SQL с меткой даты и затем выполнит выделенный запрос ( F5). Если вы не привыкли к сценариям AHK, начальная точка с запятой - это комментарий.

;CTRL+SHIFT+R to run a query that is first saved off
^+r::
;Copy
Send, ^c
; Set variables
EnvGet, HomeDir, USERPROFILE
FormatTime, DateString,,yyyyMMdd
FormatTime, TimeString,,hhmmss
; Make a spot to save the clipboard
FileCreateDir %HomeDir%\Documents\sqlhist\%DateString%
FileAppend, %Clipboard%, %HomeDir%\Documents\sqlhist\%DateString%\%TimeString%.sql
; execute the query
Send, {f5}
Return

Самым большим ограничением является то, что этот сценарий не будет работать, если вы нажмете «Выполнить» вместо использования сочетания клавиш, и этот сценарий не сохранит весь файл - только выделенный текст. Но вы всегда можете изменить скрипт для выполнения запроса, а затем выбрать все ( CTRL+A ) перед копированием / сохранением.

Использование современного редактора с функциями поиска в файлах позволит вам осуществлять поиск в истории SQL. Вы можете даже прихотить и скопировать свои файлы в базу данных SQLite3 для запроса ваших запросов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.