Як переглянути історію запитів у студії управління SQL Server


159

Чи зберігається історія запитів у деяких файлах журналу? Якщо так, чи можете ви сказати мені, як знайти їх місцезнаходження? Якщо ні, чи можете ви дати мені якусь пораду, як це бачити?


1
http://www.ssmstoolspack.com/ надає вікно історії, якщо ви цього хочете.
ТІ

Відповіді:


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, як згадується у відповіді Еда Гарпера - хоча це не безкоштовно в SQL Server 2012+. Або ви можете налаштувати легку трасування, відфільтровану за вашим входом або ім'ям хоста (але, будь ласка, використовуйте для цього слід сервера, а не Profiler).


Як коментує @ Ненад-Живкович, може бути корисним приєднатися 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
Ненад Живкович

Не працює з sql сервером 2000 і працює з sql сервером 2005
Durai Amuthan.H

@Duraiamuthan Ну, питання про студію менеджменту, тож слід припустити 2005+. У 2000 році не було студії управління, вона мала аналізатор запитів. 2000 також багато років не підтримує. Якщо ви хочете вирішити цю проблему для SQL Server 2000, вам, мабуть, слід задати нове запитання, позначене цією конкретною версією (якщо дублікат не існує, що я не перевіряв).
Аарон Бертран

1
@AaronBertrand Мій коментар доповнює ваш answer.it допоможе іншим
Durai Amuthan.H

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

49

Пізній, але, сподіваємось, корисний, оскільки додає більше деталей ...

Немає можливості побачити запити, виконані в SSMS за замовчуванням. Однак є кілька варіантів.

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

Для цього можна використовувати сторонні інструменти, такі як ApexSQL Log і SQL Log Rescue (безкоштовно, але лише SQL 2000). Ознайомтесь з цією темою для отримання більш детальної інформації тут Провідник / аналізатор журналу транзакцій SQL Server

Профілер SQL Server - найкраще підходить, якщо ви просто хочете почати аудит і вам не цікаво, що сталося раніше. Переконайтеся, що ви використовуєте фільтри для вибору лише необхідних транзакцій. Інакше дуже швидко ви отримаєте тону даних.

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

Тригери - найкраще підходять, якщо ви хочете захопити DML (крім вибору) та зберігати їх десь у базі даних


Створення файлу слідів у програмі SQL Server Profiler ( 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

Ви можете знайти ідентифікатор для кожної події трасування, стовпців тощо з книг Інтернет; просто шукати 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

Це повинно показувати вам час та дату, коли виконувався запит



2

Історію запитів можна переглянути за допомогою системних представлень:

  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] .

Існують також різні сторонні рішення для історії запитів. Я використовую менеджер запитів від Dbeaver : введіть тут опис зображення та Історію виконання запитів із SQL Tools , який вбудований у SSMS : введіть тут опис зображення


1

Ця функція не існує у вікні SSMS.

Якщо ви використовуєте SSMS 18 або новішу версію, ви можете спробувати SSMSPlus.

Він має функцію історії запитів.

https://github.com/akarzazi/SSMSPlus

Відмова: Я автор.


0

ви можете використовувати "Автоматично генерувати сценарій при кожному збереженні", якщо ви використовуєте студію управління. Це, звичайно, не лісозаготівля. Перевірте, чи корисно для вас ..;)


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.