如何在SQL Server Management Studio中查看查询历史记录

时间:2011-03-14 14:06:43

标签: sql-server logging ssms

查询历史记录是否存储在某些日志文件中?如果是的话,你能告诉我如何找到他们的位置吗?如果没有,你能就如何看待它给我任何建议吗?

13 个答案:

答案 0 :(得分:198)

[由于this question可能会被重复关闭。]

如果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\

否则,您需要使用其他内容来帮助您保存查询历史记录,例如Ed Harper's answer中提到的SSMS工具包 - 尽管它在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;

答案 1 :(得分:49)

迟到但希望有用,因为它增加了更多细节...

默认情况下无法在SSMS中查看执行的查询。但有几种选择。

阅读交易日志 - 这不是一件容易的事,因为它采用专有格式。但是,如果您需要查看历史执行的查询(SELECT除外),这是唯一的方法。

您可以使用第三方工具,例如ApexSQL LogSQL Log Rescue(仅限免费的SQL 2000)。有关详细信息,请查看此主题SQL Server Transaction Log Explorer/Analyzer

SQL Server探查器 - 如果您只是想开始审核并且您对之前发生的事情不感兴趣,那么它是最合适的。确保使用过滤器仅选择所需的事务。否则你很快就会得到大量的数据。

SQL Server跟踪 - 如果您想捕获所有或大多数命令并将它们保存在以后可以解析的跟踪文件中,则最适合。

触发器 - 如果你想捕获DML(select除外)并将它们存储在数据库中的某个地方,最适合

答案 2 :(得分:16)

SSMS tools pack增加了记录执行历史记录的功能。

答案 3 :(得分:6)

正如其他人所说,您可以使用SQL事件探查器,但您也可以通过sp_trace_ *系统存储过程利用它的功能。例如,对于所有接管的查询,此SQL代码段(至少在2000年;我认为对于SQL 2008是相同的,但您必须仔细检查)捕获RPC:CompletedSQL: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

您可以在联机丛书中找到每个跟踪事件,列等的ID;只需搜索sp_trace_createsp_trace_seteventsp_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'是跟踪ID(由sp_trace_create报告,第一个脚本在上面启动)。

您可以检查以查看正在运行的跟踪:

select * from ::fn_trace_getinfo(default)

我唯一要注意的是 - 我不知道这会给你的系统增加多少负载;它会添加一些,但“some”的大小可能取决于服务器的繁忙程度。

答案 4 :(得分:6)

可以使用系统视图查看查询历史记录:

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

例如,使用以下查询:

using (WebClient web = new WebClient())
{
     string url = web.DownloadString("http://api.openweathermap.org/data/2.5/weather?q=" + lookedCity + "&mode=xml&units="+tempType+"&appid=70e1d7c5");

     XmlDocument doc = new XmlDocument();
     doc.LoadXml(url);

     temp = doc.DocumentElement.SelectSingleNode("temperature").Attributes["value"].Value;
     country = doc.DocumentElement.SelectSingleNode("country").Value;
     city = doc.DocumentElement.SelectSingleNode("city").Attributes["name"].Value;
}

使用以下脚本可以查看当前正在运行的查询:

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%'

此请求显示所有活动请求以及所有明确阻止活动请求的请求。

所有这些脚本和其他有用的脚本都作为SRV数据库中的表示形式实现,并且可以自由分发。 例如,第一个脚本来自视图[inf].[vBigQuery],第二个脚本来自视图[inf].[vRequests]

对于查询历史记录,还有各种第三方解决方案。 我使用Query Manager中的Dbeaverenter image description hereQuery Execution History中的SQL Tools(嵌入SSMS中): enter image description here

答案 5 :(得分:5)

系统不会以这种方式记录查询。如果您知道您希望提前执行此操作,则可以使用SQL事件探查器记录正在进行的操作并在Profiler运行期间跟踪查询。

答案 6 :(得分:3)

如果需要,可以按SQL Profiler监控SQL查询

答案 7 :(得分:3)

我使用以下查询来跟踪未启用跟踪分析器的SQL服务器上的应用程序活动。 该方法使用Query Store(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()) 

答案 8 :(得分:1)

如果您想要通过SMSS执行的查询的历史记录。

您可能想尝试SSMSPlus。

https://github.com/akarzazi/SSMSPlus

此功能在SSMS中不存在。

您需要使用SSMS 18或更高版本。

免责声明:我是作者。

答案 9 :(得分:0)

如果您使用的是管理工作室,则可以使用“在每次保存时自动生成脚本”。 这不一定是记录。 检查对你有用..;)

答案 10 :(得分:0)

如果您感兴趣的查询是间歇性失败的动态查询,则可以在创建动态语句时将SQL以及日期时间和用户记录在表中。它将在逐个案例的基础上完成,因为它需要特定的编程才能发生,并且需要一个较小的额外处理时间,所以它只针对您最关心的那些少数查询。但是当你试图找出它为什么每月失败一次时,记录执行的特定语句真的会有所帮助。动态查询很难进行彻底的测试,有时你会得到一个特定的输入值,这个输入值只是在创建SQL时进行日志记录,这通常是查看构建的sql中特定内容的最佳方法。

答案 11 :(得分:0)

一种开箱即用的方法是在AutoHotKey中编写解决方案的脚本。我使用它,它并不完美,但是可以正常使用并且是免费的。本质上,此脚本将热键分配给 CTRL + SHIFT + R ,这将在SSMS中复制选定的SQL( 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数据库中以查询查询。

答案 12 :(得分:0)

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

这应该向您显示查询运行的时间和日期