@@ -1469,6 +1469,119 @@ ORDER BY
14691469 return items ;
14701470 }
14711471
1472+ public async Task < List < TracePatternDetailItem > > GetTracePatternHistoryAsync ( string databaseName , string queryPattern , int hoursBack = 24 , DateTime ? fromDate = null , DateTime ? toDate = null )
1473+ {
1474+ var items = new List < TracePatternDetailItem > ( ) ;
1475+
1476+ await using var tc = await OpenThrottledConnectionAsync ( ) ;
1477+ var connection = tc . Connection ;
1478+
1479+ string timeFilter = fromDate . HasValue && toDate . HasValue
1480+ ? "ta.end_time >= @from_date AND ta.end_time <= @to_date"
1481+ : "ta.end_time >= DATEADD(HOUR, @hours_back, SYSDATETIME())" ;
1482+
1483+ /* Trace events can appear in multiple collection cycles because the trace file
1484+ retains events until it rolls over. Deduplicate by partitioning on the event's
1485+ natural key (end_time + duration + cpu + reads) and keeping only the first row. */
1486+ string query = $@ "
1487+ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
1488+
1489+ WITH
1490+ numbered AS
1491+ (
1492+ SELECT
1493+ ta.analysis_id,
1494+ ta.collection_time,
1495+ ta.event_name,
1496+ ta.database_name,
1497+ ta.login_name,
1498+ ta.application_name,
1499+ ta.host_name,
1500+ ta.spid,
1501+ ta.duration_ms,
1502+ ta.cpu_ms,
1503+ ta.reads,
1504+ ta.writes,
1505+ ta.row_counts,
1506+ ta.start_time,
1507+ ta.end_time,
1508+ sql_text = LEFT(ta.sql_text, 4000),
1509+ ta.object_id,
1510+ rn = ROW_NUMBER() OVER
1511+ (
1512+ PARTITION BY
1513+ ta.end_time,
1514+ ta.duration_ms,
1515+ ta.cpu_ms,
1516+ ta.reads,
1517+ ta.spid
1518+ ORDER BY
1519+ ta.collection_time
1520+ )
1521+ FROM collect.trace_analysis AS ta
1522+ WHERE ta.database_name = @database_name
1523+ AND LEFT(ta.sql_text, 200) = @query_pattern
1524+ AND { timeFilter }
1525+ )
1526+ SELECT
1527+ analysis_id,
1528+ collection_time,
1529+ event_name,
1530+ database_name,
1531+ login_name,
1532+ application_name,
1533+ host_name,
1534+ spid,
1535+ duration_ms,
1536+ cpu_ms,
1537+ reads,
1538+ writes,
1539+ row_counts,
1540+ start_time,
1541+ end_time,
1542+ sql_text,
1543+ object_id
1544+ FROM numbered
1545+ WHERE rn = 1
1546+ ORDER BY
1547+ end_time DESC;" ;
1548+
1549+ using var command = new SqlCommand ( query , connection ) ;
1550+ command . CommandTimeout = 120 ;
1551+ command . Parameters . Add ( new SqlParameter ( "@database_name" , SqlDbType . NVarChar , 128 ) { Value = databaseName } ) ;
1552+ command . Parameters . Add ( new SqlParameter ( "@query_pattern" , SqlDbType . NVarChar , 200 ) { Value = queryPattern } ) ;
1553+ command . Parameters . Add ( new SqlParameter ( "@hours_back" , SqlDbType . Int ) { Value = - hoursBack } ) ;
1554+ if ( fromDate . HasValue ) command . Parameters . Add ( new SqlParameter ( "@from_date" , SqlDbType . DateTime2 ) { Value = fromDate . Value } ) ;
1555+ if ( toDate . HasValue ) command . Parameters . Add ( new SqlParameter ( "@to_date" , SqlDbType . DateTime2 ) { Value = toDate . Value } ) ;
1556+
1557+ using var reader = await command . ExecuteReaderAsync ( ) ;
1558+ while ( await reader . ReadAsync ( ) )
1559+ {
1560+ items . Add ( new TracePatternDetailItem
1561+ {
1562+ AnalysisId = reader . GetInt64 ( 0 ) ,
1563+ CollectionTime = reader . GetDateTime ( 1 ) ,
1564+ EventName = reader . IsDBNull ( 2 ) ? string . Empty : reader . GetString ( 2 ) ,
1565+ DatabaseName = reader . IsDBNull ( 3 ) ? null : reader . GetString ( 3 ) ,
1566+ LoginName = reader . IsDBNull ( 4 ) ? null : reader . GetString ( 4 ) ,
1567+ ApplicationName = reader . IsDBNull ( 5 ) ? null : reader . GetString ( 5 ) ,
1568+ HostName = reader . IsDBNull ( 6 ) ? null : reader . GetString ( 6 ) ,
1569+ Spid = reader . IsDBNull ( 7 ) ? null : reader . GetInt32 ( 7 ) ,
1570+ DurationMs = reader . IsDBNull ( 8 ) ? null : reader . GetInt64 ( 8 ) ,
1571+ CpuMs = reader . IsDBNull ( 9 ) ? null : reader . GetInt64 ( 9 ) ,
1572+ Reads = reader . IsDBNull ( 10 ) ? null : reader . GetInt64 ( 10 ) ,
1573+ Writes = reader . IsDBNull ( 11 ) ? null : reader . GetInt64 ( 11 ) ,
1574+ RowCounts = reader . IsDBNull ( 12 ) ? null : reader . GetInt64 ( 12 ) ,
1575+ StartTime = reader . IsDBNull ( 13 ) ? null : reader . GetDateTime ( 13 ) ,
1576+ EndTime = reader . IsDBNull ( 14 ) ? null : reader . GetDateTime ( 14 ) ,
1577+ SqlText = reader . IsDBNull ( 15 ) ? null : reader . GetString ( 15 ) ,
1578+ ObjectId = reader . IsDBNull ( 16 ) ? null : reader . GetInt64 ( 16 )
1579+ } ) ;
1580+ }
1581+
1582+ return items ;
1583+ }
1584+
14721585 public async Task < List < BlockingDeadlockStatsItem > > GetBlockingDeadlockStatsAsync ( int hoursBack = 24 , DateTime ? fromDate = null , DateTime ? toDate = null )
14731586 {
14741587 var items = new List < BlockingDeadlockStatsItem > ( ) ;
0 commit comments