/*--------------------------------------------------------------------- $Header: /abasql/aba_lockinfo_sqlmm_sp3.sp 8 06-05-23 21:27 Sommar $ This SP lists locking information for all active processes, that is processes that have a lock or are not AWAITING COMMAND. Information about all locked objects are included, as well the last command sent from the client. Note that this command is tacked out afterwards with DBCC INPUTBUFFER, and may be out of sync with the rest of the data. The original source for the SP was taken from the undocumented system procedure sp_lockinfo. This version works only in SQL2000 SP3. There are separate versions for SQL6.5, SQL7 and SQL 2000 pre-SP3. $History: aba_lockinfo_sqlmm_sp3.sp $ * * ***************** Version 8 ***************** * User: Sommar Date: 06-05-23 Time: 21:27 * Updated in $/abasql * suser_name(sid) does not always return the login name, so use * sysprocess.loginame as a fallback. * * ***************** Version 7 ***************** * User: Sommar Date: 06-03-21 Time: 21:09 * Updated in $/abasql * Bug fix: I did not quote database names with quotename(), so you would * get a syntax error with databases with special characters. * * ***************** Version 6 ***************** * User: Sommar Date: 04-12-27 Time: 13:26 * Updated in $/abasql * * ***************** Version 5 ***************** * User: Sommar Date: 04-02-23 Time: 11:08 * Updated in $/abasql * Remove line break from curstmt. * * ***************** Version 4 ***************** * User: Sommar Date: 04-02-22 Time: 23:20 * Updated in $/abasql * Thorough rewrite for better performance. * * ***************** Version 3 ***************** * User: Sommar Date: 03-05-25 Time: 11:22 * Updated in $/abasql * stmt_start is an offset, so we should add 1 to it. * * ***************** Version 2 ***************** * User: Sommar Date: 02-12-21 Time: 23:25 * Updated in $/abasql * stmt_end = -1 means that current statement extents until end of text. * * ***************** Version 1 ***************** * User: Sommar Date: 02-12-21 Time: 23:08 * Created in $/abasql * * ***************** Version 10 ***************** * User: Sommar Date: 02-05-04 Time: 18:35 * Updated in $/abasql * Current time was saved in a format that later would cause conversion * error with some dateformat settings. * * ***************** Version 9 ***************** * User: Sommar Date: 02-03-24 Time: 0:39 * Updated in $/abasql * The setting of @minspid had disappeared. * * ***************** Version 8 ***************** * User: Sommar Date: 02-03-22 Time: 16:02 * Updated in $/abasql * Performance enhancements. No longer need for separate database, uses * temp tables. Lots of KEEPFIXED PLAN to avoid recompilations. * Unnecessary use of dynamic SQL removed. Support for SQL7 removed. * * ***************** Version 7 ***************** * User: Sommar Date: 01-11-26 Time: 15:38 * Updated in $/abasql * Now that's news! There might be more than one ecid in sysprocesses per * spid. Let's handle that! * * ***************** Version 6 ***************** * User: Sommar Date: 01-07-16 Time: 22:27 * Updated in $/abasql * Extensive rewrite. Default is now to group locks to reduce the amount * data when there are many locks. Also handling the case that a process * may not exist in sysprocesses. Handle also application locks. * * ***************** Version 5 ***************** * User: Sommar Date: 01-03-17 Time: 22:07 * Updated in $/abasql * Added SET QUOTED_IDENTIFIER OFF for the benefit of people outside * Abaris who might have this on. * * ***************** Version 4 ***************** * User: Sommar Date: 00-11-07 Time: 10:59 * Updated in $/projects/dbverktyg/abasql * Adaptions for SQL2000. Define processes that only hold a lock on a * databaes as passive. Translate object names per database, not per * object. Handle that last_since may overflow. * * ***************** Version 2 ***************** * User: Sommar Date: 00-02-09 Time: 13:19 * Updated in $/projects/dbverktyg/abasql * Stupid bug: last_since was 10 times too big. * * ***************** Version 1 ***************** * User: Sommar Date: 00-01-06 Time: 17:46 * Created in $/projects/dbverktyg/abasql * * ***************** Version 2 ***************** * User: Sommar Date: 99-12-21 Time: 19:39 * Updated in $/projects/dbverktyg/abasql * Hide system processes. * * ***************** Version 1 ***************** * User: Sommar Date: 99-12-21 Time: 19:33 * Created in $/projects/dbverktyg/abasql ---------------------------------------------------------------------*/ CREATE PROCEDURE aba_lockinfo @processes tinyint = 0, @details bit = 0, @fancy bit = 0 AS ------------------------------------------------------------------------ -- The following temp tables are work tables that are involved in dynamic -- SQL or INSERT EXEC, and therefore cannot be table variables. ------------------------------------------------------------------------ -- Output from DBCC INPUTBUFFER. CREATE TABLE #inputbuffer (eventtype nvarchar(30) NULL, params int NULL, eventinfo nvarchar(255) NULL) -- Holds all object to be identified. CREATE TABLE #objects (dbid smallint NOT NULL, objid int NOT NULL, indid tinyint NOT NULL, objname nvarchar(170) NULL, PRIMARY KEY CLUSTERED (dbid, objid, indid)) -- Used for the fancy result. CREATE TABLE #result ( ident int IDENTITY, spid smallint NOT NULL, ecid smallint NOT NULL, cnt int NULL, login sysname NOT NULL, prcstatus nvarchar(30) NOT NULL, command nvarchar(16) NOT NULL, dbname sysname NOT NULL, host nvarchar(128) NOT NULL, appl nvarchar(128) NOT NULL, opntrn varchar(5) NOT NULL, lvl char(3) NOT NULL, blkby varchar(5) NOT NULL, locktype nvarchar(70) NOT NULL, ownertype nvarchar(70) NOT NULL, object nvarchar(170) NULL, rsctype nvarchar(70) NOT NULL, lstatus nvarchar(70) NOT NULL, waittime varchar(10) NOT NULL, waittype binary(2) NULL, cpu varchar(10) NOT NULL, physio varchar(10) NOT NULL, memusg varchar(10) NOT NULL, now char(12) NOT NULL, login_time char(16) NOT NULL, last_batch char(16) NOT NULL, last_since varchar(11) NOT NULL, delay varchar(10) NOT NULL, inputbuffer varchar(255) NOT NULL, current_sp nvarchar(255) NOT NULL, curstmt nvarchar(255) NOT NULL, stmtoff varchar(15) NOT NULL, last bit NOT NULL DEFAULT 0) ------------------------------------------------------------------------ -- Then table variables for locks and processes. Input from syslockinfo and -- sysprocesses augmented with other material. ------------------------------------------------------------------------ DECLARE @procs TABLE ( spid smallint NOT NULL, ecid smallint NOT NULL, active bit NOT NULL DEFAULT 1, login sysname NULL, status nvarchar(30) NULL, dbname sysname NULL, host nvarchar(128) NULL, command nvarchar(16) NULL, appl nvarchar(128) NULL, opntrn smallint NULL, blking smallint NOT NULL, blkby smallint NULL, blklvl smallint NOT NULL, waittime int NULL, waittype binary(2) NULL, cpu int NULL, physio bigint NULL, memusage int NULL, now datetime NOT NULL, login_time char(16) NULL, last_batch char(16) NULL, last_since numeric(10,3) NULL, sql_handle binary(20) NOT NULL, stmt_start int NOT NULL, stmt_end int NOT NULL, current_sp int NULL, curdbid smallint NULL, curstmt nvarchar(255) NULL, delay int NOT NULL DEFAULT 0, inputbuffer nvarchar(255) NOT NULL DEFAULT ' ', PRIMARY KEY (spid, ecid)) DECLARE @locks TABLE ( ident int IDENTITY, spid smallint NOT NULL, ecid smallint NOT NULL, cnt int NULL, req_mode tinyint NOT NULL, rsc_type tinyint NOT NULL, req_status tinyint NOT NULL, req_ownertype smallint NOT NULL, dbid smallint NOT NULL, objid int NOT NULL, indid tinyint NOT NULL, rsc_text nchar(32) NULL, refcnt smallint NULL, activelock bit NOT NULL) -- We tried indexing, but they seem to cost more than give. -- UNIQUE NONCLUSTERED (spid, ecid, activelock, ident)) -- UNIQUE NONCLUSTERED (dbid, objid, indid, ident)) ------------------------------------------------------------------------ -- Local variables. ------------------------------------------------------------------------ DECLARE @minspid int, @objid int, @dbid smallint, @dbname sysname, @qdbname nvarchar(256), @stmt varchar(8000), @spid smallint, @sql_handle binary(20), @stmt_start int, @stmt_end int, @spidstr varchar(10), @inputbuff varchar(255), @blklvl tinyint, @now datetime ------------------------------------------------------------------------ -- All reads are dirty! The most important reason for this is tempdb..sysobjects. ------------------------------------------------------------------------ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SET NOCOUNT ON -- Processes below @minspid are system processes. SELECT @minspid = 50, @now = getdate() ------------------------------------------------------------------------ -- First caputure all locks. These can be aggregate, or we can get all of them. ------------------------------------------------------------------------ IF @details = 0 BEGIN INSERT @locks (spid, ecid, req_mode, rsc_type, req_status, req_ownertype, dbid, objid, indid, rsc_text, activelock, cnt) SELECT req_spid, req_ecid, req_mode, rsc_type, req_status, req_ownertype, rsc_dbid, rsc_objid, rsc_indid, CASE rsc_type WHEN 10 THEN rsc_text END, CASE WHEN rsc_type = 2 AND req_status = 1 THEN 0 ELSE 1 END, COUNT(*) FROM master.dbo.syslockinfo GROUP BY req_spid, req_ecid, req_mode, rsc_type, req_status, req_ownertype, rsc_dbid, rsc_objid, rsc_indid, CASE rsc_type WHEN 10 THEN rsc_text END, CASE WHEN rsc_type = 2 AND req_status = 1 THEN 0 ELSE 1 END END ELSE BEGIN INSERT @locks (spid, ecid, req_mode, rsc_type, req_status, req_ownertype, dbid, objid, indid, rsc_text, refcnt, activelock) SELECT req_spid, req_ecid, req_mode, rsc_type, req_status, req_ownertype, rsc_dbid, rsc_objid, rsc_indid, rsc_text, req_refcnt, CASE WHEN rsc_type = 2 AND req_status = 1 THEN 0 ELSE 1 END FROM master.dbo.syslockinfo END ------------------------------------------------------------------------ -- Then get the processes. We filter here for active processes once for all ------------------------------------------------------------------------ INSERT @procs(spid, ecid, login, status, dbname, host, command, appl, opntrn, blking, blkby, blklvl, waittime, waittype, cpu, physio, memusage, now, login_time, last_batch, last_since, sql_handle, stmt_start, stmt_end) SELECT p.spid, p.ecid, coalesce(suser_sname(p.sid), p.loginame), rtrim(p.status), CASE WHEN p.dbid > 0 THEN db_name(p.dbid) ELSE '' END, rtrim(p.hostname), rtrim(p.cmd), rtrim(p.program_name), p.open_tran, 0, p.blocked, 0, p.waittime, p.waittype, p.cpu, p.physical_io, p.memusage, @now, convert(char(7), p.login_time, 12) + convert(char(8), p.login_time, 8), convert(char(7), p.last_batch, 12) + convert(char(8), p.last_batch, 8), CASE WHEN datediff(DAY, p.last_batch, @now) > 20 THEN NULL ELSE datediff(MS, p.last_batch, @now) / 1000.000 END, sql_handle, stmt_start, stmt_end FROM master.dbo.sysprocesses p WHERE @processes > 0 OR (upper(p.cmd) <> 'AWAITING COMMAND' AND p.spid >= @minspid AND p.spid <> @@spid) OR p.open_tran > 0 OR p.blocked > 0 OR (EXISTS (SELECT * FROM @locks l WHERE l.spid = p.spid AND l.activelock = 1) AND spid <> @@spid) ------------------------------------------------------------------------ -- Mark inactive processes; this is only interesting if @processes = 1, -- because with @processes = 0 we only have active now. ------------------------------------------------------------------------ IF @processes = 1 BEGIN UPDATE @procs SET active = 0 FROM @procs p WHERE NOT EXISTS (SELECT * FROM @locks l WHERE p.spid = l.spid AND p.ecid = l.ecid AND l.activelock = 1 AND p.spid <> @@spid AND p.spid >= @minspid) AND (p.command = 'AWAITING COMMAND' OR p.spid < @minspid OR p.spid = @@spid) AND p.blkby = 0 END ------------------------------------------------------------------------ -- Get input buffers and fn_get_sql data. Note that only the main thread, -- ecid = 0 is of interest. ------------------------------------------------------------------------ DECLARE C1 CURSOR LOCAL FOR SELECT str(spid), spid, sql_handle, stmt_start, stmt_end FROM @procs WHERE (@processes = 2 OR active = 1) AND ecid = 0 AND login IS NOT NULL OPEN C1 WHILE 1 = 1 BEGIN FETCH C1 INTO @spidstr, @spid, @sql_handle, @stmt_start, @stmt_end IF @@fetch_status <> 0 BREAK DELETE #inputbuffer INSERT #inputbuffer EXEC ('DBCC INPUTBUFFER (' + @spidstr + ') WITH NO_INFOMSGS') SELECT @inputbuff = ' ' SELECT @inputbuff = rtrim(eventinfo) FROM #inputbuffer -- Replace line breaks with spaces. SET @inputbuff = replace(@inputbuff, char(10) + char(13), ' ') SET @inputbuff = replace(@inputbuff, char(10), ' ') SET @inputbuff = replace(@inputbuff, char(13), ' ') IF @sql_handle <> 0x0 BEGIN SELECT @objid = objectid, @dbid = dbid, @stmt = substring( CASE WHEN @stmt_start >= 0 THEN substring( text, (@stmt_start + 2)/2, CASE @stmt_end WHEN -1 THEN 255 ELSE (@stmt_end - @stmt_start + 2) / 2 END) END, 1, 255) FROM ::fn_get_sql(@sql_handle) SET @stmt = replace(@stmt, char(10) + char(13), ' ') SET @stmt = replace(@stmt, char(10), ' ') SET @stmt = replace(@stmt, char(13), ' ') END ELSE SELECT @stmt = '', @objid = NULL, @dbid = NULL UPDATE @procs SET inputbuffer = coalesce(@inputbuff, ''), delay = datediff(ms, now, @now), current_sp = @objid, curdbid = @dbid, curstmt = @stmt FROM @procs p WHERE spid = @spid AND ecid = 0 END DEALLOCATE C1 ------------------------------------------------------------------------ -- Delete inactive processes from @locks. ------------------------------------------------------------------------ IF @processes = 0 BEGIN DELETE @locks FROM @locks l WHERE NOT EXISTS (SELECT * FROM @procs p WHERE p.spid = l.spid AND p.active = 1) END ------------------------------------------------------------------------ -- Get name of objects. Need to do this per database. ------------------------------------------------------------------------ INSERT #objects (dbid, objid, indid) SELECT dbid, objid, indid FROM @locks WHERE dbid > 0 AND objid > 0 UNION SELECT curdbid, current_sp, 0 FROM @procs WHERE curdbid > 0 AND current_sp > 0 DECLARE C2 CURSOR LOCAL FOR SELECT DISTINCT dbid, db_name(dbid), quotename(db_name(dbid)) FROM #objects OPEN C2 WHILE 1 = 1 BEGIN FETCH C2 INTO @dbid, @dbname, @qdbname IF @@fetch_status <> 0 BREAK -- Set database.owner.name(.index) of all objects in #objects. SELECT @stmt = ' UPDATE #objects SET objname = ''' + @dbname + '.'' + u.name + ''.'' + o.name + CASE coalesce(t.indid, 0) WHEN 0 THEN '''' ELSE ''.'' + i.name END FROM #objects t JOIN ' + @qdbname + '.dbo.sysobjects o ON t.objid = o.id JOIN ' + @qdbname + '.dbo.sysusers u ON u.uid = o.uid LEFT JOIN ' + @qdbname + '.dbo.sysindexes i ON t.indid = i.indid AND t.objid = i.id WHERE t.dbid = ' + str(@dbid) + ' AND t.objid > 0 ' EXEC (@stmt) END DEALLOCATE C2 ------------------------------------------------------------------------ -- Flag blocking and blocked processes ------------------------------------------------------------------------ UPDATE @procs SET blking = 1 FROM @procs p WHERE EXISTS (SELECT * FROM @procs p2 WHERE p.spid = p2.blkby) UPDATE @procs SET blklvl = 1 WHERE blking = 1 AND blkby = 0 SELECT @blklvl = 1 -- Find out place in the queue for blocked processes. WHILE EXISTS (SELECT * FROM @procs WHERE blkby > 0 AND blklvl = 0) AND @blklvl < 20 BEGIN UPDATE p1 SET blklvl = @blklvl + 1 FROM @procs p1 JOIN @procs p2 ON p1.blkby = p2.spid WHERE p1.blkby > 0 AND p1.blklvl = 0 AND p2.blklvl = @blklvl SELECT @blklvl = @blklvl + 1 END ------------------------------------------------------------------------ -- For Plain results we are ready to return now. ------------------------------------------------------------------------ IF @fancy = 0 BEGIN SELECT spid = coalesce(p.spid, l.spid), ecid = coalesce(p.ecid, l.ecid), cnt = CASE @details WHEN 0 THEN coalesce(l.cnt, 0) WHEN 1 THEN coalesce(l.refcnt, 0) END, login = coalesce(p.login, ''), prcstatus = coalesce(p.status, ''), command = coalesce(p.command, ''), dbname = coalesce(p.dbname, ''), host = coalesce(p.host, ''), appl = coalesce(p.appl, ''), opntrn = coalesce(convert(varchar(5), p.opntrn), ''), lvl = CASE coalesce(p.blklvl, 0) WHEN 0 THEN '' WHEN 1 THEN '!!' ELSE convert(varchar(3), p.blklvl - 1) END, blkby = CASE coalesce(p.blkby, 0) WHEN 0 THEN '' ELSE convert(varchar(5), p.blkby) END, locktype = coalesce(v1.name, ''), ownertype = coalesce(v2.name, ''), object = CASE WHEN l.rsc_type = 10 THEN rtrim(l.rsc_text) WHEN l.rsc_type = 2 THEN rtrim(db_name(l.dbid)) WHEN l.rsc_type IS NOT NULL THEN coalesce(o1.objname, db_name(l.dbid) + '.MISSING?') ELSE '' END, rsctype = coalesce(v3.name, ''), lstatus = coalesce(v4.name, ''), waittime = CASE coalesce(p.waittime, 0) WHEN 0 THEN '' ELSE convert(varchar(10), p.waittime) END, p.waittype, cpu = coalesce(convert(varchar(10), p.cpu), ''), physio = coalesce(convert(varchar(10), p.physio), ''), memusg = coalesce(convert(varchar(10), p.memusage), ''), now = convert(char(12), p.now, 114), login_time = coalesce(p.login_time, ''), last_batch = coalesce(p.last_batch, ''), last_since = coalesce(str(p.last_since, 11, 3), ''), delay = coalesce(convert(varchar(10), p.delay), ''), inputbuffer = coalesce(p.inputbuffer, ''), current_sp = coalesce(o2.objname, ''), curstmt = coalesce(p.curstmt, ''), stmtoff = coalesce(ltrim(str(stmt_start/2)), '') + '/' + coalesce(ltrim(str(stmt_end/2)), '') FROM (@procs p LEFT JOIN #objects o2 ON p.curdbid = o2.dbid AND p.current_sp = o2.objid AND o2.indid = 0) FULL JOIN (@locks l LEFT JOIN master.dbo.spt_values v1 ON v1.number = l.req_mode + 1 AND v1.type = 'L' LEFT JOIN master.dbo.spt_values v2 ON v2.number = l.req_ownertype AND v2.type = 'LO' LEFT JOIN master.dbo.spt_values v3 ON v3.number = l.rsc_type AND v3.type = 'LR' LEFT JOIN master.dbo.spt_values v4 ON v4.number = l.req_status AND v4.type = 'LS' LEFT JOIN #objects o1 ON l.dbid = o1.dbid AND l.objid = o1.objid AND l.indid = o1.indid) ON p.spid = l.spid AND p.ecid = l.ecid ORDER BY spid, ecid, lstatus DESC, object END ELSE BEGIN ------------------------------------------------------------------------ -- For fancy result, we save to #result, and to find suitable lengths. ------------------------------------------------------------------------ DECLARE @spidlen varchar(5), @ecidlen varchar(5), @cntlen varchar(5), @loginlen varchar(5), @statuslen varchar(5), @dbnamelen varchar(5), @hostlen varchar(5), @cmdlen varchar(5), @appllen varchar(5), @waitlen varchar(5), @waitreslen varchar(5), @locktlen varchar(5), @restlen varchar(5), @lkstatlen varchar(5), @lkobjlen varchar(5), @ownertlen varchar(5), @cpulen varchar(5), @physiolen varchar(5), @memlen varchar(5), @delaylen varchar(5), @curobjlen varchar(5), @stmtlen varchar(5), @stmtofflen varchar(5), @inputlen varchar(5) INSERT #result (spid, ecid, cnt, login, prcstatus, command, dbname, host, appl, opntrn, lvl, blkby, locktype, ownertype, object, rsctype, lstatus, waittime, p.waittype, cpu, physio, memusg, now, login_time, last_batch, last_since, delay, inputbuffer, current_sp, curstmt, stmtoff) SELECT spid = coalesce(p.spid, l.spid), ecid = coalesce(p.ecid, l.ecid), cnt = CASE @details WHEN 0 THEN coalesce(l.cnt, 0) WHEN 1 THEN coalesce(l.refcnt, 0) END, login = coalesce(p.login, ''), prcstatus = coalesce(p.status, ''), command = coalesce(p.command, ''), dbname = coalesce(p.dbname, ''), host = coalesce(p.host, ''), appl = coalesce(p.appl, ''), opntrn = coalesce(convert(varchar(5), p.opntrn), ''), lvl = CASE coalesce(p.blklvl, 0) WHEN 0 THEN '' WHEN 1 THEN '!!' ELSE convert(varchar(3), p.blklvl - 1) END, blkby = CASE coalesce(p.blkby, 0) WHEN 0 THEN '' ELSE convert(varchar(5), p.blkby) END, locktype = coalesce(v1.name, ''), ownertype = coalesce(v2.name, ''), object = CASE WHEN l.rsc_type = 10 THEN rtrim(l.rsc_text) WHEN l.rsc_type = 2 THEN rtrim(db_name(l.dbid)) WHEN l.rsc_type IS NOT NULL THEN coalesce(o1.objname, db_name(l.dbid) + '.MISSING?') ELSE '' END, rsctype = coalesce(v3.name, ''), lstatus = coalesce(v4.name, ''), waittime = CASE coalesce(p.waittime, 0) WHEN 0 THEN '' ELSE convert(varchar(10), p.waittime) END, p.waittype, cpu = coalesce(convert(varchar(10), p.cpu), ''), physio = coalesce(convert(varchar(10), p.physio), ''), memusg = coalesce(convert(varchar(10), p.memusage), ''), now = convert(char(12), p.now, 114), login_time = coalesce(p.login_time, ''), last_batch = coalesce(p.last_batch, ''), last_since = coalesce(str(p.last_since, 11, 3), ''), delay = coalesce(convert(varchar(10), p.delay), ''), inputbuffer = coalesce(p.inputbuffer, ''), current_sp = coalesce(o2.objname, ''), curstmt = coalesce(p.curstmt, ''), stmtoff = coalesce(ltrim(str(p.stmt_start / 2)), '') + '/' + coalesce(ltrim(str(p.stmt_end/2)), '') FROM (@procs p LEFT JOIN #objects o2 ON p.curdbid = o2.dbid AND p.current_sp = o2.objid AND o2.indid = 0) FULL JOIN (@locks l LEFT JOIN master.dbo.spt_values v1 ON v1.number = l.req_mode + 1 AND v1.type = 'L' LEFT JOIN master.dbo.spt_values v2 ON v2.number = l.req_ownertype AND v2.type = 'LO' LEFT JOIN master.dbo.spt_values v3 ON v3.number = l.rsc_type AND v3.type = 'LR' LEFT JOIN master.dbo.spt_values v4 ON v4.number = l.req_status AND v4.type = 'LS' LEFT JOIN #objects o1 ON l.dbid = o1.dbid AND l.objid = o1.objid AND l.indid = o1.indid) ON p.spid = l.spid AND p.ecid = l.ecid ORDER BY spid, ecid, lstatus DESC, object -- Mark last row. UPDATE #result SET last = 1 FROM #result r1 JOIN (SELECT spid, ident = MAX(ident) FROM #result GROUP BY spid) AS r2 ON r2.ident = r1.ident OPTION (KEEPFIXED PLAN) -- Get all maxlengths SELECT @spidlen = convert(varchar(5), coalesce(max(len(ltrim(str(spid)))), 1)), @ecidlen = convert(varchar(5), coalesce(max(len(ltrim(str(ecid)))), 1)), @cntlen = convert(varchar(5), coalesce(max(len(ltrim(str(cnt)))), 1)), @loginlen = convert(varchar(5), coalesce(nullif(max(len(login)), 0), 1)), @cntlen = convert(varchar(5), coalesce(nullif(max(len(ltrim(str(cnt)))), 0), 1)), @statuslen = convert(varchar(5), coalesce(nullif(max(len(prcstatus)), 0), 1)), @dbnamelen = convert(varchar(5), coalesce(nullif(max(len(dbname)), 0), 1)), @hostlen = convert(varchar(5), coalesce(nullif(max(len(host)), 0), 1)), @cmdlen = convert(varchar(5), coalesce(nullif(max(len(command)), 0), 1)), @appllen = convert(varchar(5), coalesce(nullif(max(len(appl)), 0), 1)), @waitlen = convert(varchar(5), coalesce(nullif(max(len(waittime)), 0), 1)), @locktlen = convert(varchar(5), coalesce(nullif(max(len(locktype)), 0), 1)), @lkobjlen = convert(varchar(5), coalesce(nullif(max(len(object)), 0), 1)), @ownertlen = convert(varchar(5), coalesce(nullif(max(len(ownertype)), 0), 1)), @restlen = convert(varchar(5), coalesce(nullif(max(len(rsctype)), 0), 1)), @lkstatlen = convert(varchar(5), coalesce(nullif(max(len(lstatus)), 0), 1)), @cpulen = convert(varchar(5), coalesce(nullif(max(len(cpu)), 0), 1)), @physiolen = convert(varchar(5), coalesce(nullif(max(len(physio)), 0), 1)), @memlen = convert(varchar(5), coalesce(nullif(max(len(memusg)), 0), 1)), @delaylen = convert(varchar(5), coalesce(nullif(max(len(delay)), 0), 1)), @curobjlen = convert(varchar(5), coalesce(nullif(max(len(current_sp)), 0), 1)), @inputlen = convert(varchar(5), coalesce(nullif(max(len(inputbuffer)), 0), 1)), @stmtlen = convert(varchar(5), coalesce(nullif(max(len(curstmt)), 0), 1)), @stmtofflen = convert(varchar(5), coalesce(nullif(max(len(stmtoff)), 0), 1)) FROM #result OPTION (KEEPFIXED PLAN) -- Return the #results table with dynamic lengths. EXEC ('SELECT spid = str(spid, ' + @spidlen + '), ecid = str(ecid, ' + @ecidlen + '), cnt = convert(varchar( ' + @cntlen + '), cnt), login = convert(varchar( ' + @loginlen + '), login), prcstatus = convert(varchar( ' + @statuslen + '), prcstatus), command = convert(varchar( ' + @cmdlen + '), command), dbname = convert(varchar( ' + @dbnamelen + '), dbname), host = convert(varchar( ' + @hostlen + '), host), appl = convert(varchar( ' + @appllen + '), appl), opntrn, lvl, blkby, locktype = convert(varchar( ' + @locktlen + '), locktype), ownertype = convert(varchar( ' + @ownertlen + '), ownertype), object = convert(varchar( ' + @lkobjlen + '), object), rsctype = convert(varchar( ' + @restlen + '), rsctype), lstatus = convert(varchar( ' + @lkstatlen + '), lstatus), waittime = convert(varchar( ' + @waitlen + '), waittime), waittype, cpu = convert(varchar( ' + @cpulen + '), cpu), io = convert(varchar( ' + @physiolen + '), physio), memusg = convert(varchar( ' + @memlen + '), memusg), now, login_time, last_batch, last_since, delay = convert(varchar( ' + @delaylen + '), delay), intputbuffer = convert(varchar( ' + @inputlen + '), inputbuffer), current_sp = convert(varchar( ' + @curobjlen + '), current_sp), curstmt = convert(varchar( ' + @stmtlen + '), curstmt), stmtoff = convert(varchar( ' + @stmtofflen + '), stmtoff), CASE last WHEN 1 THEN char(10) ELSE '' '' END FROM #result ORDER BY ident') END