L'historique des requêtes est-il stocké dans certains fichiers journaux? Si oui, pouvez-vous me dire comment trouver leur emplacement? Sinon, pouvez-vous me donner des conseils sur la façon de le voir?
L'historique des requêtes est-il stocké dans certains fichiers journaux? Si oui, pouvez-vous me dire comment trouver leur emplacement? Sinon, pouvez-vous me donner des conseils sur la façon de le voir?
Réponses:
[Étant donné que cette question sera probablement fermée en double.]
Si SQL Server n'a pas été redémarré (et que le plan n'a pas été expulsé, etc.), vous pourrez peut-être trouver la requête dans le cache du plan.
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%';
Si vous avez perdu le fichier en raison de la panne de Management Studio, vous pourrez peut-être trouver les fichiers de récupération ici:
C:\Users\<you>\Documents\SQL Server Management Studio\Backup Files\
Sinon, vous devrez utiliser quelque chose d'autre à l'avenir pour vous aider à enregistrer votre historique de requêtes, comme SSMS Tools Pack comme mentionné dans la réponse d'Ed Harper - bien que ce ne soit pas gratuit dans SQL Server 2012+. Ou vous pouvez configurer un traçage léger filtré sur votre nom de connexion ou votre nom d'hôte (mais veuillez utiliser une trace côté serveur, pas Profiler, pour cela).
Comme l'a commenté @ Nenad-Zivkovic, il pourrait être utile de se joindre à nous sys.dm_exec_query_stats
et de commander en 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;
sys.dm_exec_query_stats
et rechercher ou commander parlast_execution_time
En retard mais j'espère utile car il ajoute plus de détails ...
Il n'y a aucun moyen de voir les requêtes exécutées dans SSMS par défaut. Il existe cependant plusieurs options.
Lire le journal des transactions - ce n'est pas une chose facile à faire car il est dans un format propriétaire. Cependant, si vous avez besoin de voir les requêtes qui ont été exécutées historiquement (à l'exception de SELECT), c'est le seul moyen.
Vous pouvez utiliser des outils tiers pour cela, tels que ApexSQL Log et SQL Log Rescue (gratuit mais SQL 2000 uniquement). Consultez ce fil pour plus de détails ici SQL Server Transaction Log Explorer / Analyzer
Profileur SQL Server - le mieux adapté si vous souhaitez simplement commencer l'audit et que vous n'êtes pas intéressé par ce qui s'est passé précédemment. Assurez-vous d'utiliser des filtres pour sélectionner uniquement les transactions dont vous avez besoin. Sinon, vous vous retrouverez très rapidement avec une tonne de données.
Suivi SQL Server - le mieux adapté si vous souhaitez capturer la totalité ou la plupart des commandes et les conserver dans un fichier de trace qui peut être analysé ultérieurement.
Déclencheurs - mieux adaptés si vous souhaitez capturer DML (sauf select) et les stocker quelque part dans la base de données
Le pack d'outils SSMS ajoute des fonctionnalités pour enregistrer l'historique d'exécution, entre autres.
Comme d'autres l'ont noté, vous pouvez utiliser SQL Profiler, mais vous pouvez également tirer parti de ses fonctionnalités via les procédures stockées système sp_trace_ *. Par exemple, cet extrait de code SQL (sur 2000 au moins; je pense que c'est la même chose pour SQL 2008 mais vous devrez revérifier) capture RPC:Completed
et SQL:BatchCompleted
événements pour toutes les requêtes qui prennent plus de 10 secondes à s'exécuter, et enregistre la sortie dans un fichier trace que vous pouvez ouvrir dans le profileur SQL à une date ultérieure:
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
Vous pouvez trouver l'ID de chaque événement de trace, colonnes, etc. dans la documentation en ligne; recherche juste pour les sp_trace_create , sp_trace_setevent et sp_trace_setfiler sprocs. Vous pouvez ensuite contrôler la trace comme suit:
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
... où '15' est l'ID de trace (comme indiqué par sp_trace_create, que le premier script lance, ci-dessus).
Vous pouvez vérifier avec quelles traces s'exécutent:
select * from ::fn_trace_getinfo(default)
La seule chose que je dirai avec prudence - je ne sais pas combien de charge cela imposera à votre système; cela en ajoutera, mais la taille de ce "certains" dépend probablement de l'occupation de votre serveur.
J'utilise la requête ci-dessous pour tracer l'activité des applications sur un serveur SQL sur lequel le profileur de trace n'est pas activé. La méthode utilise Query Store (SQL Server 2016+) au lieu des DMV. Cela donne une meilleure capacité à examiner les données historiques, ainsi que des recherches plus rapides. Il est très efficace de capturer des requêtes à exécution courte qui ne peuvent pas être capturées par 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())
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
Cela devrait vous montrer l'heure et la date auxquelles une requête a été exécutée
Vous pouvez surveiller les requêtes SQL par SQL Profiler si vous en avez besoin
L'historique des requêtes peut être visualisé à l'aide des vues système:
Par exemple, en utilisant la requête suivante:
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%'
Les requêtes en cours d'exécution peuvent être visualisées à l'aide du script suivant:
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])
Cette demande affiche toutes les demandes actives et toutes les demandes qui bloquent explicitement les demandes actives.
Tous ces scripts, ainsi que d'autres, sont implémentés sous forme de représentations dans la base de données SRV , qui est distribuée librement. Par exemple, le premier script provenait de la vue [inf]. [VBigQuery] et le second de la vue [inf]. [VRequests] .
Il existe également diverses solutions tierces pour l'historique des requêtes. J'utilise Query Manager de Dbeaver : et Query Execution History de SQL Tools , qui est intégré dans SSMS :
Cette fonctionnalité n'existe pas prête à l'emploi dans SSMS.
Si vous utilisez SSMS 18 ou plus récent, vous pouvez essayer SSMSPlus.
Il a une fonction d'historique des requêtes.
https://github.com/akarzazi/SSMSPlus
Avertissement: je suis l'auteur.
Si les requêtes qui vous intéressent sont des requêtes dynamiques qui échouent par intermittence, vous pouvez enregistrer le SQL, la date / heure et l'utilisateur dans une table au moment de la création de l'instruction dynamique. Cela serait fait au cas par cas, car cela nécessite une programmation spécifique et cela prend un peu plus de temps de traitement, alors ne le faites que pour les quelques requêtes qui vous préoccupent le plus. Mais avoir un journal des instructions spécifiques exécutées peut vraiment aider lorsque vous essayez de découvrir pourquoi cela échoue une fois par mois seulement. Les requêtes dynamiques sont difficiles à tester de manière approfondie et parfois vous obtenez une valeur d'entrée spécifique qui ne fonctionnera tout simplement pas et cette journalisation au moment de la création du SQL est souvent le meilleur moyen de voir ce qui était spécifiquement dans le SQL qui a été construit.
Une méthode un peu originale serait de créer un script pour une solution dans AutoHotKey. Je l'utilise, et ce n'est pas parfait, mais fonctionne et est gratuit. Essentiellement, ce script attribue un raccourci clavier à CTRL+ SHIFT+ Rqui copiera le SQL sélectionné dans SSMS ( CTRL+ C), enregistrera un fichier SQL d'horodatage, puis exécutera la requête en surbrillance ( F5). Si vous n'êtes pas habitué aux scripts AHK, le premier point-virgule est un commentaire.
;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
Les plus grandes limitations sont que ce script ne fonctionnera pas si vous cliquez sur "Exécuter" plutôt que d'utiliser le raccourci clavier, et ce script ne sauvegardera pas le fichier entier - juste le texte sélectionné. Mais, vous pouvez toujours modifier le script pour exécuter la requête, puis sélectionner tout ( CTRL+ A) avant la copie / sauvegarde.
L'utilisation d'un éditeur moderne avec des fonctionnalités de «recherche dans les fichiers» vous permettra de rechercher votre historique SQL. Vous pouvez même obtenir de la fantaisie et gratter vos fichiers dans une base de données SQLite3 pour interroger vos requêtes.