October 2, 2008 at 12:10 pm
To find the exact SQL statement executed at a point in time I can run the script below, which I found in BOL and various blogs. Can someone explain why statement_start_offset is divided by 2 and then 1 is added and statement_start_offset is divided by 2 and then 1 is added? If statement_start_offset is the number of characters into the currently executing batch or stored procedure at which the currently executing statement starts and statement_end_offset is the number of characters into the currently executing batch or stored procedure at which the currently executing statement ends, why not just subtract statement_start_offset from statement_end_offset? I'm certain there is a good explanation, but I can't figure it out.
SELECTa.session_id,
SUBSTRING(c.text, (a.statement_start_offset/2)+1,
((CASE a.statement_end_offset
WHEN -1 THEN DATALENGTH(c.text)
ELSE a.statement_end_offset
END - a.statement_start_offset)/2) + 1) as statement_text
FROM sys.dm_exec_requests a
JOIN sys.dm_exec_sessions b
on a.session_id = b.session_id
CROSS APPLY fn_get_sql (a.sql_handle) c
Thanks, Dave
October 3, 2008 at 8:05 am
Perhaps to strip off comments around the actual command text?
Replace the top line of your query with
/* test */SELECT a.session_id,
a.statement_start_offset AS StartO,
a.statement_end_offset AS EndO,
c.TEXT,
and compare. Play with the length of the comment.
October 3, 2008 at 2:45 pm
I found a blog that pointed me in the right direction. Division by 2 is needed because the text data is stored by Microsoft as Unicode.
Thanks, Dave
October 6, 2008 at 6:55 am
Hey now, my response demonstrated that several hours before you found that other site.
🙂
For the same reason, the line that reads
WHEN -1 THEN DATALENGTH(c.text)
should read
WHEN -1 THEN DATALENGTH(c.text) * 2
so that the final query is reported in full.
Thanks for sharing a nice query for checking on slowness without perhaps having to launch the profiler.
October 6, 2008 at 7:07 am
No, because datalength already takes unicode/non-unicode into account.
SELECT DATALENGTH('abc') returns 3
SELECT DATALENGTH(N'abc') returns 6, because it's a unicode string and hence requires 2 bytes/character
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
October 6, 2008 at 7:14 am
Run DBADave's original query as is and check the result; it won't return the full query.
Add the "*2" and you'll get the whole thing.
I also ran with a second query running at the same time in another query window before I posted; same results.
October 6, 2008 at 7:25 am
That's not because of the datalength needs to be multiplied by 2. It's because the /2 is outside the CASE statement but only applies to the ELSE portion of the case.
Try this one which does work correctly.
SELECT a.session_id,
SUBSTRING(c.text, (a.statement_start_offset/2)+1,
((CASE a.statement_end_offset
WHEN -1 THEN DATALENGTH(c.text)
ELSE (a.statement_end_offset - a.statement_start_offset)/2
END)) + 1) as statement_text
FROM sys.dm_exec_requests a
JOIN sys.dm_exec_sessions b
on a.session_id = b.session_id
CROSS APPLY fn_get_sql (a.sql_handle) c
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
October 6, 2008 at 7:52 am
I hadn't noticed that. No point multiplying and dividing by 2.
I should've dug further when I saw it was you, Gail. You always do nice work.
October 6, 2008 at 10:50 am
I also noted some occurrences where statement_end_offset came back as 0, so I made the change for my purposes to treat it like -1. Otherwise you get back only one char of the current statement.
(I also add a grab on DB Id, which we needed here, and dropped the join on dm_exec_requests.)
SELECT a.session_id, c.dbid,
SUBSTRING(c.text, (a.statement_start_offset/2)+1,
((CASE WHEN a.statement_end_offset < 1
THEN DATALENGTH(c.text)
ELSE (a.statement_end_offset - a.statement_start_offset)/2
END)) + 1) as statement_text
FROM sys.dm_exec_requests a
--JOIN sys.dm_exec_sessions b
--on a.session_id = b.session_id
CROSS APPLY fn_get_sql (a.sql_handle) c
October 6, 2008 at 1:57 pm
I'll make the change to my code, but I found that if I substitute CROSS APPLY fn_get_sql (a.sql_handle) c with
CROSS APPLY sys.dm_exec_sql_text (a.sql_handle) c the original logic works. Any idea why that would be?
Dave
October 6, 2008 at 2:08 pm
Again because of the varchar/nvarchar
In fn_get_sql, the text column is of type text, is a non-unicode value, so datalength returns the length of the string and hence dividing that by 2 gives a wrong answer. In exec_sql_text, the text column is nvarchar(max), so the datalength is twice the length of the string and hence does need to be divided by 2
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
October 6, 2008 at 2:19 pm
Thanks. I just reviewed my original notes and the initial code I copied from Microsoft did reference dm_exec_sql_text and not fn_get_sql. Somewhere in my testing I swapped the two, thus leading to the confusion. Thanks for the clarification.
Dave
July 20, 2010 at 3:55 am
This is a similar version which I use. It removes the join to Requests and Sessions, and uses sys.dm_exec_query_stats to retrieve all plans from the cache.
Please let me know if anything seems incorrect.
SELECT a.execution_count, a.plan_handle,
SUBSTRING(c.text, (a.statement_start_offset/2)+1,
((CASE a.statement_end_offset
WHEN -1 THEN DATALENGTH(c.text)
ELSE a.statement_end_offset
END - a.statement_start_offset)) + 1) as statement_text
FROM sys.dm_exec_query_stats a
CROSS APPLY sys.dm_exec_sql_text (a.sql_handle) c
July 20, 2010 at 5:35 am
Please note: 2 year old thread.
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
July 20, 2010 at 7:31 am
Thanks Gail. Didn't notice that.
Would you agree that the query I posted is suitable for querying plans in the cache and then seperating the statements using the offsets please?
thanks.
Viewing 15 posts - 1 through 14 (of 14 total)
You must be logged in to reply to this topic. Login to reply