Skip to content

Commit c470fc0

Browse files
authored
Merge pull request #2259 from DanielSQLDBA/dev
sp_BlitzLock: add output tables
2 parents e52b3b6 + 5d5ddde commit c470fc0

File tree

1 file changed

+331
-10
lines changed

1 file changed

+331
-10
lines changed

sp_BlitzLock.sql

Lines changed: 331 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ ALTER PROCEDURE dbo.sp_BlitzLock
1818
@Help BIT = 0,
1919
@Version VARCHAR(30) = NULL OUTPUT,
2020
@VersionDate DATETIME = NULL OUTPUT,
21-
@VersionCheckMode BIT = 0
21+
@VersionCheckMode BIT = 0,
22+
@OutputDatabaseName NVARCHAR(256) = NULL ,
23+
@OutputSchemaName NVARCHAR(256) = 'dbo' , --dito as below
24+
@OutputTableName NVARCHAR(256) = 'BlitzLock' --put a standard here no need to check later in the script
2225
)
2326
WITH RECOMPILE
2427
AS
@@ -64,7 +67,9 @@ END;
6467
6568
@EventSessionPath: If you want to point this at an XE session rather than the system health session.
6669
67-
70+
@OutputDatabaseName: If you want to output information to a specific database
71+
@OutputSchemaName: Specify a schema name to output information to a specific Schema
72+
@OutputTableName: Specify table name to to output information to a specific table
6873
6974
To learn more, visit http://FirstResponderKit.org where you can download new
7075
versions for free, watch training videos on how it works, get more info on
@@ -179,8 +184,110 @@ You need to use an Azure storage account, and the path has to look like this: ht
179184
finding_group NVARCHAR(100),
180185
finding NVARCHAR(4000)
181186
);
182-
183-
DECLARE @d VARCHAR(40), @StringToExecute NVARCHAR(4000);
187+
DECLARE @d VARCHAR(40), @StringToExecute NVARCHAR(4000),@StringToExecuteParams NVARCHAR(500),@r NVARCHAR(200),@OutputTableFindings NVARCHAR(100);
188+
DECLARE @ServerName NVARCHAR(256)
189+
DECLARE @OutputDatabaseCheck BIT;
190+
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
191+
SET @OutputTableFindings = '[BlitzLockFindings]'
192+
SET @ServerName = (select @@ServerName)
193+
if(@OutputDatabaseName is not null)
194+
BEGIN --if databaseName is set do some sanity checks and put [] around def.
195+
if( (select name from sys.databases where name=@OutputDatabaseName) is null ) --if database is invalid raiserror and set bitcheck
196+
BEGIN
197+
RAISERROR('Database Name for output of table is invalid please correct, Output to Table will not be preformed', 0, 1, @d) WITH NOWAIT;
198+
set @OutputDatabaseCheck = -1 -- -1 invalid/false, 0 = good/true
199+
END
200+
ELSE
201+
BEGIN
202+
set @OutputDatabaseCheck = 0
203+
select @StringToExecute = N'select @r = name from ' + '' + @OutputDatabaseName +
204+
'' + '.sys.objects where type_desc=''USER_TABLE'' and name=' + '''' + @OutputTableName + '''',
205+
@StringToExecuteParams = N'@OutputDatabaseName NVARCHAR(200),@OutputTableName NVARCHAR(200),@r NVARCHAR(200) OUTPUT'
206+
exec sp_executesql @StringToExecute,@StringToExecuteParams,@OutputDatabaseName,@OutputTableName,@r OUTPUT
207+
--put covers around all before.
208+
SELECT @OutputDatabaseName = QUOTENAME(@OutputDatabaseName),
209+
@OutputTableName = QUOTENAME(@OutputTableName),
210+
@OutputSchemaName = QUOTENAME(@OutputSchemaName)
211+
if(@r is null) --if it is null there is no table, create it from above execution
212+
BEGIN
213+
select @StringToExecute = N'use ' + @OutputDatabaseName + ';create table ' + @OutputSchemaName + '.' + @OutputTableName + ' (
214+
ServerName NVARCHAR(256),
215+
deadlock_type NVARCHAR(256),
216+
event_date datetime,
217+
Database_Name NVARCHAR(256),
218+
deadlock_group NVARCHAR(256),
219+
query XML,
220+
object_names XML,
221+
isolation_level NVARCHAR(256),
222+
owner_mode NVARCHAR(256),
223+
waiter_mode NVARCHAR(256),
224+
transaction_count bigint,
225+
login_name NVARCHAR(256),
226+
host_name NVARCHAR(256),
227+
client_app NVARCHAR(256),
228+
wait_time BIGINT,
229+
priority smallint,
230+
log_used BIGINT,
231+
last_tran_started datetime,
232+
last_batch_started datetime,
233+
last_batch_completed datetime,
234+
transaction_name NVARCHAR(256),
235+
owner_waiter_type NVARCHAR(256),
236+
owner_activity NVARCHAR(256),
237+
owner_waiter_activity NVARCHAR(256),
238+
owner_merging NVARCHAR(256),
239+
owner_spilling NVARCHAR(256),
240+
owner_waiting_to_close NVARCHAR(256),
241+
waiter_waiter_type NVARCHAR(256),
242+
waiter_owner_activity NVARCHAR(256),
243+
waiter_waiter_activity NVARCHAR(256),
244+
waiter_merging NVARCHAR(256),
245+
waiter_spilling NVARCHAR(256),
246+
waiter_waiting_to_close NVARCHAR(256),
247+
deadlock_graph XML)',
248+
@StringToExecuteParams = N'@OutputDatabaseName NVARCHAR(200),@OutputSchemaName NVARCHAR(100),@OutputTableName NVARCHAR(200)'
249+
exec sp_executesql @StringToExecute, @StringToExecuteParams,@OutputDatabaseName,@OutputSchemaName,@OutputTableName
250+
--table created.
251+
select @StringToExecute = N'select @r = name from ' + '' + @OutputDatabaseName +
252+
'' + '.sys.objects where type_desc=''USER_TABLE'' and name=''BlitzLockFindings''',
253+
@StringToExecuteParams = N'@OutputDatabaseName NVARCHAR(200),@r NVARCHAR(200) OUTPUT'
254+
exec sp_executesql @StringToExecute,@StringToExecuteParams,@OutputDatabaseName,@r OUTPUT
255+
if(@r is null) --if table does not excist
256+
BEGIN
257+
select @OutputTableFindings=N'[BlitzLockFindings]',
258+
@StringToExecute = N'use ' + @OutputDatabaseName + ';create table ' + @OutputSchemaName + '.' + @OutputTableFindings + ' (
259+
ServerName NVARCHAR(256),
260+
check_id INT,
261+
database_name NVARCHAR(256),
262+
object_name NVARCHAR(1000),
263+
finding_group NVARCHAR(100),
264+
finding NVARCHAR(4000))',
265+
@StringToExecuteParams = N'@OutputDatabaseName NVARCHAR(200),@OutputSchemaName NVARCHAR(100),@OutputTableFindings NVARCHAR(200)'
266+
exec sp_executesql @StringToExecute, @StringToExecuteParams, @OutputDatabaseName,@OutputSchemaName,@OutputTableFindings
267+
268+
END
269+
270+
END
271+
--create synonym for deadlockfindings.
272+
if((select name from sys.objects where name='DeadlockFindings' and type_desc='SYNONYM')IS NOT NULL)
273+
BEGIN
274+
RAISERROR('found synonym', 0, 1) WITH NOWAIT;
275+
drop synonym DeadlockFindings;
276+
END
277+
set @StringToExecute = 'CREATE SYNONYM DeadlockFindings FOR ' + @OutputDatabaseName + '.' + @OutputSchemaName + '.' + @OutputTableFindings;
278+
exec sp_executesql @StringToExecute
279+
280+
--create synonym for deadlock table.
281+
if((select name from sys.objects where name='DeadLockTbl' and type_desc='SYNONYM') IS NOT NULL)
282+
BEGIN
283+
drop SYNONYM DeadLockTbl;
284+
END
285+
set @StringToExecute = 'CREATE SYNONYM DeadLockTbl FOR ' + @OutputDatabaseName + '.' + @OutputSchemaName + '.' + @OutputTableName;
286+
exec sp_executesql @StringToExecute
287+
288+
END
289+
END
290+
184291

185292
CREATE TABLE #t (id INT NOT NULL);
186293

@@ -1036,7 +1143,9 @@ You need to use an Azure storage account, and the path has to look like this: ht
10361143
--CREATE CLUSTERED INDEX cx_whatever ON #deadlock_process (event_date, id);
10371144
--CREATE CLUSTERED INDEX cx_whatever ON #deadlock_resource_parallel (event_date, owner_id);
10381145
--CREATE CLUSTERED INDEX cx_whatever ON #deadlock_owner_waiter (event_date, owner_id, waiter_id);
1039-
1146+
IF(@OutputDatabaseCheck = 0)
1147+
BEGIN
1148+
10401149
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
10411150
RAISERROR('Results 1 %s', 0, 1, @d) WITH NOWAIT;
10421151
WITH deadlocks
@@ -1141,7 +1250,44 @@ You need to use an Azure storage account, and the path has to look like this: ht
11411250
CROSS APPLY (SELECT TOP 1 * FROM #deadlock_resource_parallel AS drp WHERE drp.owner_id = dp.id AND drp.wait_type = 'e_waitPipeGetRow' ORDER BY drp.event_date) AS caw
11421251
WHERE dp.victim_id IS NULL
11431252
AND dp.login_name IS NOT NULL)
1144-
SELECT d.deadlock_type,
1253+
insert into DeadLockTbl (
1254+
ServerName,
1255+
deadlock_type,
1256+
event_date,
1257+
database_name,
1258+
deadlock_group,
1259+
query,
1260+
object_names,
1261+
isolation_level,
1262+
owner_mode,
1263+
waiter_mode,
1264+
transaction_count,
1265+
login_name,
1266+
host_name,
1267+
client_app,
1268+
wait_time,
1269+
priority,
1270+
log_used,
1271+
last_tran_started,
1272+
last_batch_started,
1273+
last_batch_completed,
1274+
transaction_name,
1275+
owner_waiter_type,
1276+
owner_activity,
1277+
owner_waiter_activity,
1278+
owner_merging,
1279+
owner_spilling,
1280+
owner_waiting_to_close,
1281+
waiter_waiter_type,
1282+
waiter_owner_activity,
1283+
waiter_waiter_activity,
1284+
waiter_merging,
1285+
waiter_spilling,
1286+
waiter_waiting_to_close,
1287+
deadlock_graph
1288+
)
1289+
SELECT @ServerName,
1290+
d.deadlock_type,
11451291
d.event_date,
11461292
DB_NAME(d.database_id) AS database_name,
11471293
'Deadlock #'
@@ -1192,17 +1338,192 @@ You need to use an Azure storage account, and the path has to look like this: ht
11921338
AND (d.login_name = @LoginName OR @LoginName IS NULL)
11931339
ORDER BY d.event_date, is_victim DESC
11941340
OPTION ( RECOMPILE );
1341+
1342+
drop SYNONYM DeadLockTbl; --done insert into blitzlock table going to insert into findings table first create synonym.
11951343

1344+
-- RAISERROR('att deadlock findings', 0, 1) WITH NOWAIT;
1345+
11961346

1197-
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
1347+
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
11981348
RAISERROR('Findings %s', 0, 1, @d) WITH NOWAIT;
1199-
SELECT df.check_id, df.database_name, df.object_name, df.finding_group, df.finding
1349+
1350+
Insert into DeadlockFindings (ServerName,check_id,database_name,object_name,finding_group,finding)
1351+
SELECT @ServerName,df.check_id, df.database_name, df.object_name, df.finding_group, df.finding
12001352
FROM #deadlock_findings AS df
12011353
ORDER BY df.check_id
12021354
OPTION ( RECOMPILE );
12031355

1204-
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
1205-
RAISERROR('Done %s', 0, 1, @d) WITH NOWAIT;
1356+
drop SYNONYM DeadlockFindings; --done with inserting.
1357+
END
1358+
ELSE --Output to database is not set output to client app
1359+
BEGIN
1360+
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
1361+
RAISERROR('Results 1 %s', 0, 1, @d) WITH NOWAIT;
1362+
WITH deadlocks
1363+
AS ( SELECT N'Regular Deadlock' AS deadlock_type,
1364+
dp.event_date,
1365+
dp.id,
1366+
dp.victim_id,
1367+
dp.database_id,
1368+
dp.priority,
1369+
dp.log_used,
1370+
dp.wait_resource COLLATE DATABASE_DEFAULT AS wait_resource,
1371+
CONVERT(
1372+
XML,
1373+
STUFF(( SELECT DISTINCT NCHAR(10)
1374+
+ N' <object>'
1375+
+ ISNULL(c.object_name, N'')
1376+
+ N'</object> ' COLLATE DATABASE_DEFAULT AS object_name
1377+
FROM #deadlock_owner_waiter AS c
1378+
WHERE (dp.id = c.owner_id
1379+
OR dp.victim_id = c.waiter_id)
1380+
AND dp.event_date = c.event_date
1381+
FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(4000)'),
1382+
1, 1, N'')) AS object_names,
1383+
dp.wait_time,
1384+
dp.transaction_name,
1385+
dp.last_tran_started,
1386+
dp.last_batch_started,
1387+
dp.last_batch_completed,
1388+
dp.lock_mode,
1389+
dp.transaction_count,
1390+
dp.client_app,
1391+
dp.host_name,
1392+
dp.login_name,
1393+
dp.isolation_level,
1394+
dp.process_xml.value('(//process/inputbuf/text())[1]', 'NVARCHAR(MAX)') AS inputbuf,
1395+
ROW_NUMBER() OVER ( PARTITION BY dp.event_date, dp.id ORDER BY dp.event_date ) AS dn,
1396+
DENSE_RANK() OVER ( ORDER BY dp.event_date ) AS en,
1397+
ROW_NUMBER() OVER ( PARTITION BY dp.event_date ORDER BY dp.event_date ) -1 AS qn,
1398+
dp.is_victim,
1399+
ISNULL(dp.owner_mode, '-') AS owner_mode,
1400+
NULL AS owner_waiter_type,
1401+
NULL AS owner_activity,
1402+
NULL AS owner_waiter_activity,
1403+
NULL AS owner_merging,
1404+
NULL AS owner_spilling,
1405+
NULL AS owner_waiting_to_close,
1406+
ISNULL(dp.waiter_mode, '-') AS waiter_mode,
1407+
NULL AS waiter_waiter_type,
1408+
NULL AS waiter_owner_activity,
1409+
NULL AS waiter_waiter_activity,
1410+
NULL AS waiter_merging,
1411+
NULL AS waiter_spilling,
1412+
NULL AS waiter_waiting_to_close,
1413+
dp.deadlock_graph
1414+
FROM #deadlock_process AS dp
1415+
WHERE dp.victim_id IS NOT NULL
1416+
1417+
UNION ALL
1418+
1419+
SELECT N'Parallel Deadlock' AS deadlock_type,
1420+
dp.event_date,
1421+
dp.id,
1422+
dp.victim_id,
1423+
dp.database_id,
1424+
dp.priority,
1425+
dp.log_used,
1426+
dp.wait_resource COLLATE DATABASE_DEFAULT,
1427+
CONVERT(XML, N'parallel_deadlock' COLLATE DATABASE_DEFAULT) AS object_names,
1428+
dp.wait_time,
1429+
dp.transaction_name,
1430+
dp.last_tran_started,
1431+
dp.last_batch_started,
1432+
dp.last_batch_completed,
1433+
dp.lock_mode,
1434+
dp.transaction_count,
1435+
dp.client_app,
1436+
dp.host_name,
1437+
dp.login_name,
1438+
dp.isolation_level,
1439+
dp.process_xml.value('(//process/inputbuf/text())[1]', 'NVARCHAR(MAX)') AS inputbuf,
1440+
ROW_NUMBER() OVER ( PARTITION BY dp.event_date, dp.id ORDER BY dp.event_date ) AS dn,
1441+
DENSE_RANK() OVER ( ORDER BY dp.event_date ) AS en,
1442+
ROW_NUMBER() OVER ( PARTITION BY dp.event_date ORDER BY dp.event_date ) -1 AS qn,
1443+
NULL AS is_victim,
1444+
cao.wait_type COLLATE DATABASE_DEFAULT AS owner_mode,
1445+
cao.waiter_type AS owner_waiter_type,
1446+
cao.owner_activity AS owner_activity,
1447+
cao.waiter_activity AS owner_waiter_activity,
1448+
cao.merging AS owner_merging,
1449+
cao.spilling AS owner_spilling,
1450+
cao.waiting_to_close AS owner_waiting_to_close,
1451+
caw.wait_type COLLATE DATABASE_DEFAULT AS waiter_mode,
1452+
caw.waiter_type AS waiter_waiter_type,
1453+
caw.owner_activity AS waiter_owner_activity,
1454+
caw.waiter_activity AS waiter_waiter_activity,
1455+
caw.merging AS waiter_merging,
1456+
caw.spilling AS waiter_spilling,
1457+
caw.waiting_to_close AS waiter_waiting_to_close,
1458+
dp.deadlock_graph
1459+
FROM #deadlock_process AS dp
1460+
CROSS APPLY (SELECT TOP 1 * FROM #deadlock_resource_parallel AS drp WHERE drp.owner_id = dp.id AND drp.wait_type = 'e_waitPipeNewRow' ORDER BY drp.event_date) AS cao
1461+
CROSS APPLY (SELECT TOP 1 * FROM #deadlock_resource_parallel AS drp WHERE drp.owner_id = dp.id AND drp.wait_type = 'e_waitPipeGetRow' ORDER BY drp.event_date) AS caw
1462+
WHERE dp.victim_id IS NULL
1463+
AND dp.login_name IS NOT NULL)
1464+
SELECT d.deadlock_type,
1465+
d.event_date,
1466+
DB_NAME(d.database_id) AS database_name,
1467+
'Deadlock #'
1468+
+ CONVERT(NVARCHAR(10), d.en)
1469+
+ ', Query #'
1470+
+ CASE WHEN d.qn = 0 THEN N'1' ELSE CONVERT(NVARCHAR(10), d.qn) END
1471+
+ CASE WHEN d.is_victim = 1 THEN ' - VICTIM' ELSE '' END
1472+
AS deadlock_group,
1473+
CONVERT(XML, N'<inputbuf><![CDATA[' + d.inputbuf + N']]></inputbuf>') AS query,
1474+
d.object_names,
1475+
d.isolation_level,
1476+
d.owner_mode,
1477+
d.waiter_mode,
1478+
d.transaction_count,
1479+
d.login_name,
1480+
d.host_name,
1481+
d.client_app,
1482+
d.wait_time,
1483+
d.priority,
1484+
d.log_used,
1485+
d.last_tran_started,
1486+
d.last_batch_started,
1487+
d.last_batch_completed,
1488+
d.transaction_name,
1489+
/*These columns will be NULL for regular (non-parallel) deadlocks*/
1490+
d.owner_waiter_type,
1491+
d.owner_activity,
1492+
d.owner_waiter_activity,
1493+
d.owner_merging,
1494+
d.owner_spilling,
1495+
d.owner_waiting_to_close,
1496+
d.waiter_waiter_type,
1497+
d.waiter_owner_activity,
1498+
d.waiter_waiter_activity,
1499+
d.waiter_merging,
1500+
d.waiter_spilling,
1501+
d.waiter_waiting_to_close,
1502+
d.deadlock_graph
1503+
FROM deadlocks AS d
1504+
WHERE d.dn = 1
1505+
AND d.en < CASE WHEN d.deadlock_type = N'Parallel Deadlock' THEN 2 ELSE 2147483647 END
1506+
AND (DB_NAME(d.database_id) = @DatabaseName OR @DatabaseName IS NULL)
1507+
AND (d.event_date >= @StartDate OR @StartDate IS NULL)
1508+
AND (d.event_date < @EndDate OR @EndDate IS NULL)
1509+
AND (CONVERT(NVARCHAR(MAX), d.object_names) LIKE '%' + @ObjectName + '%' OR @ObjectName IS NULL)
1510+
AND (d.client_app = @AppName OR @AppName IS NULL)
1511+
AND (d.host_name = @HostName OR @HostName IS NULL)
1512+
AND (d.login_name = @LoginName OR @LoginName IS NULL)
1513+
ORDER BY d.event_date, is_victim DESC
1514+
OPTION ( RECOMPILE );
1515+
1516+
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
1517+
RAISERROR('Findings %s', 0, 1, @d) WITH NOWAIT;
1518+
SELECT df.check_id, df.database_name, df.object_name, df.finding_group, df.finding
1519+
FROM #deadlock_findings AS df
1520+
ORDER BY df.check_id
1521+
OPTION ( RECOMPILE );
1522+
1523+
SET @d = CONVERT(VARCHAR(40), GETDATE(), 109);
1524+
RAISERROR('Done %s', 0, 1, @d) WITH NOWAIT;
1525+
END --done with output to client app.
1526+
12061527

12071528

12081529
IF @Debug = 1

0 commit comments

Comments
 (0)