How to check if a sql variable is declared? - sql-server

In MS SQL Server. Please don't tell me to ctrl-f. I don't have access to the entire query, but I'm composing the columns that depend on whether certain variable is declared.
Thanks.
Edit:
I'm working with some weird query engine. I need to write the select columns part and the engine will take care of the rest (hopefully). But in some cases this engine will declare variables (thankfully I will know the variable names), and in other cases it doesn't. I need to compose my columns to take these variables when they are declared, and give default values when these variables are not declared.

Given the limitations of my understanding of what you're running (I'm deciphering the word problem to mean that your "query engine" is actually a "query generation engine" so, something like an ORM) you could observe what's occurring on the server in this scenario with the following query:
select
sql_handle,
st.text
from sys.dm_exec_requests r
cross apply sys.dm_exec_sql_text(r.sql_handle) st
where session_id <> ##SPID
and st.text like '%#<<parameter_name>>%';
The statement needs to have begun execution to be able to catch it. Depending on a multitude of situations, you may be able to pull it from query stats, too:
This will also get you the query plan (if it has one), but note that it will also pull stats for itself as well as the query above so you'll need to be discerning when you look at the outer and statement text values:
select
text,
SUBSTRING(
st.text,
(qs.statement_start_offset / 2) + 1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(st.TEXT)
ELSE qs.statement_end_offset
END -
qs.statement_start_offset) / 2) + 1)
AS statement_text,
plan_generation_num, creation_time, last_execution_time, execution_count
,total_worker_time, last_worker_time, min_worker_time, max_worker_time,
total_physical_reads, min_physical_reads, max_physical_reads, last_physical_reads,
total_logical_writes, min_logical_writes, max_logical_writes, last_logical_writes,
total_logical_reads, min_logical_reads, max_logical_reads, last_logical_reads,
total_elapsed_time, last_elapsed_time, min_elapsed_time, max_elapsed_time,
total_rows,last_rows,min_rows,max_rows
,qp.*
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text(qs.sql_handle) st
outer apply sys.dm_exec_query_plan(qs.plan_handle) qp
where st.text like '%#<<parameter_name>>%';

Related

Is there a way to find the line of code a query is running on in a SQL Server?

I have a long running query session, I'd like to find out what line (piece of code in a sproc) it's currently running in SQL Server?
In an active user session, there's no query plan available that I can look into for a user process who ran "select into" or "Insert" inside a sproc. I tried to look into the sys.dm_exec_requests to see if there's such a linenum column for that active session but there isn't.
Thanks for any help.
sys.dm_exec_requests only returns the sql_handle, you need to pass that to dm_exec_sql_text to get the text. Then you can use some calculation to work out the text of the running statement
SELECT
SUBSTRING(st.text, (r.statement_start_offset / 2) + 1,
((CASE r.statement_end_offset
WHEN -1 THEN DATALENGTH(st.text)
ELSE r.statement_end_offset
END - r.statement_start_offset) / 2) + 1) AS statement_text
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
WHERE r.session_id = #someSPID;

SQL Server: IsMSShipped = 0 but system SPs still returned

SELECT DISTINCT
OBJECT_SCHEMA_NAME (sc.object_id) as "schema", OBJECT_NAME(sc.object_id) as "name", sc.*
-- FROM syscomments sc
FROM sys.sql_modules sc
WHERE "Definition" LIKE '%raiserror%'
and
OBJECTPROPERTY(object_id, 'IsMSShipped') = 0
and
OBJECT_NAME(sc.object_id) like '%diagram%'
Why is this query returning these SPs? Aren't they from Microsoft?
sp_helpdiagramdefinition
sp_creatediagram
sp_renamediagram
sp_alterdiagram
sp_dropdiagram
IsMSShipped is set to 1 for any object that was created during SQL Server's installation. The Diagram objects are optional and are only added to a database after the initial installation.
In other words, although they are from MS, they are not Shipped from MS (at least not as MS is defining "Shipped").
Yes I know, it's dumb, everyone gets tripped up by this at least once. They should have called it something like IsMSInstalled instead. Just goes to show the importance of picking good names.
The SOP way to handle this is to also filter on the schema ("sys" is always schema_id 4).

Possible to detect cursors (nested cursors) within T-SQL code?

I'm hoping to find a way to sniff out potentially inefficient T-SQL within stored procedures, in this case detecting not just cursors in stored procedures, but preferably nested cursors.
With the script below based on sys.dm_sql_referenced_entities, from a given starting stored procedure I can see a recursive downstream call stack, including a column indicating whether the text CURSOR was found within the procedure definition.
This is helpful, but it isn't capable of telling me:
whether more than one cursor exists within a procedure
whether nested cursors are used (and of course, the truly perfect solution for that would also have to detect a call to stored procedure containing a cursor, from within a cursor)
Being able to do this I think is probably beyond the abilities of querying sys tables, and involves parsing the SQL itself - does anyone know of a technique or tool that could accomplish this, or perhaps an entirely different approach that could tell me the same information.
DECLARE #procname varchar(30)
SET #procname='dbo.some_root_procedure_name'
;WITH CTE([DB],[OBJ],[INDENTED_OBJ],[SCH],[lvl],[indexof_cursor],[referenced_object_definition])
AS
(
SELECT referenced_database_name AS [DB],referenced_entity_name AS [OBJ],
cast(space(0) + referenced_entity_name as varchar(max)) AS [INDENTED_OBJ],
referenced_schema_name AS [SCH],0 AS [lvl]
,charindex('cursor',object_definition(referenced_id)) as indexof_cursor
,object_definition(referenced_id) as [referenced_object_definition]
FROM sys.dm_sql_referenced_entities(#procname, 'OBJECT')
INNER JOIN sys.objects as o on o.object_id=OBJECT_ID(referenced_entity_name)
WHERE o.type IN ('P','FN','IF','TF')
UNION ALL
SELECT referenced_database_name AS [DB],referenced_entity_name AS [OBJ],
cast(space(([lvl]+1)*2) + referenced_entity_name as varchar(max)) AS [INDENTED_OBJ],
referenced_schema_name AS [SCH],[lvl]+1 as [lvl]
,charindex('cursor',object_definition(referenced_id)) as indexof_cursor
,object_definition(referenced_id) as [referenced_object_definition]
FROM CTE as c CROSS APPLY
sys.dm_sql_referenced_entities(c.SCH+'.'+c.OBJ, 'OBJECT') as ref
INNER JOIN sys.objects as o on o.object_id=OBJECT_ID(referenced_entity_name)
WHERE o.type IN ('P','FN','IF','TF') and ref.referenced_entity_name NOT IN (c.OBJ) -- Exit Condition
)
SELECT
*
FROM CTE
EDIT: I am marking this as "solved" even though I think some improvements could be made to the below solution - I think it is "good enough" for most scenarios, but I think a fully recursive solution that can traverse an "infinitely" deep call chain is possible.
Maybe there is a more efficient way, but you could search the procedure code. It's not foolproof though in that it could get some false positives, but you shouldn't miss any. It doesn't ignore comments and variable names so it's quite possible to pick up some extra stuff.
SELECT name, xtype, colid, text
into #CodeBlocks
FROM dbo.sysobjects left join .dbo.syscomments
ON dbo.sysobjects.id = .dbo.syscomments.id
where xtype = 'P'
order by 1
SELECT name,
(SELECT convert(varchar(max),text)
FROM #CodeBlocks t2
WHERE t1.name = t2.name
ORDER BY t2.colid
FOR XML PATH('')
) text
into #AllCode
FROM #CodeBlocks t1
GROUP BY name
select #AllCode.name,
case when InterProc.name is not null then
'Possible Inter-Proc Nesting'
when #AllCode.text like '%CURSOR%FOR%CURSOR%FOR%DEALLOCATE%DEALLOCATE%' then
'Possible Nested Cursor'
when #AllCode.text like '%CURSOR%FOR%CURSOR%FOR%' then
'Possible Multiple Cursor Used'
ELSE
'Possible Cursor Used'
end
from #AllCode
left join #AllCode InterProc
on InterProc.text like '%CURSOR%FOR%'
and #AllCode.text like '%CURSOR%FOR%' + InterProc.name + '%DEALLOCATE%'
where #AllCode.text like '%CURSOR%FOR%'
I found a few nested cursors on our server I didn't know about. Interesting. :)

SQL Select fields of table value function

I have a table valued function declared so that I can return several values in one go. The values are calculated using DECLARE and Maths and Date functions.
The function is structured such that it only takes a 'logged date', and a priority for issues in a support system. I honestly thought that I'd be able to select as follows:
SELECT SupportCall.*, dbo.GetSLAStatus(SupportCall.createDate, SupportCall.priority).* FROM SupportCall
I've actually ended up with:
SELECT SupportCall.*,
SLADays = (select SLADays from dbo.GetSLAStatus(SupportCall.createDate, SupportCall.priority)),
SLARecieved = (select SLAReceived from dbo.GetSLAStatus(SupportCall.createDate, SupportCall.priority)),
SLATarget = (select SLATarget from dbo.GetSLAStatus(SupportCall.createDate, SupportCall.priority)),
SLAHoursRemaining = (select SLAHoursRemaining from dbo.GetSLAStatus(SupportCall.createDate, SupportCall.priority))
From SupportCall
I can't see a possible join for an Apply (which I don't fully understand anyway).
Does anybody know whether the function calls with the same parameters will be executed once? If I'm not going to end up with lots of subqueries and function calls when the query runs, then I don't care, the code is actually quite tidy if not concise.
If there is a massive overhead, does anybody know how to select all columns from a table function of this kind (i.e. no keys, just several calculations on the same input data).
Thanks.
Mark
Don't do that! Inline queries are NEVER faster than JOINS or APPLY. Rewrite your query and check the IO. You can rewrite it something like:
SELECT SupportCall.*,
SLADays = gs.SLADays,
SLAReceived = gs.SLAReceived,
...
From SupportCall sc
CROSS APPLY dbo.GetSLAStatus(SupportCall.createDate, SupportCall.priority) gs
Can't you just do this:
SELECT C.*,
F.SLADays,
F.SLAReceived,
F.SLATarget,
F.SLAHoursRemaining
From
SupportCall C
cross apply dbo.GetSLAStatus(C.createDate, C.priority) F
I hope you're function is an inline function (e.g has a single statement that starts returns table return (...) and does not have a defined result table)

SQL Server - Missing Indexes - What would use the index?

I am using SQL Server 2008 and we are using the DMV's to find missing indexes. However, before I create the new index I am trying to figure out what proc/query is wanting that index. I want the most information I can get so I can make informed decision on my indexes. Sometimes the indexes SQL Server wants does not make sense to me. Does anyone know how I can figure out what wants it?
you could try something like this query, which lists the QueryText:
;WITH XMLNAMESPACES(DEFAULT N'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
, CachedPlans AS
(SELECT
RelOp.op.value(N'../../#NodeId', N'int') AS ParentOperationID
,RelOp.op.value(N'#NodeId', N'int') AS OperationID
,RelOp.op.value(N'#PhysicalOp', N'varchar(50)') AS PhysicalOperator
,RelOp.op.value(N'#LogicalOp', N'varchar(50)') AS LogicalOperator
,RelOp.op.value(N'#EstimatedTotalSubtreeCost ', N'float') AS EstimatedCost
,RelOp.op.value(N'#EstimateIO', N'float') AS EstimatedIO
,RelOp.op.value(N'#EstimateCPU', N'float') AS EstimatedCPU
,RelOp.op.value(N'#EstimateRows', N'float') AS EstimatedRows
,cp.plan_handle AS PlanHandle
,qp.query_plan AS QueryPlan
,st.TEXT AS QueryText
,cp.cacheobjtype AS CacheObjectType
,cp.objtype AS ObjectType
,cp.usecounts AS UseCounts
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
CROSS APPLY qp.query_plan.nodes(N'//RelOp') RelOp (op)
)
SELECT
PlanHandle
,ParentOperationID
,OperationID
,PhysicalOperator
,LogicalOperator
,UseCounts
,CacheObjectType
,ObjectType
,EstimatedCost
,EstimatedIO
,EstimatedCPU
,EstimatedRows
,QueryText
FROM CachedPlans
WHERE CacheObjectType = N'Compiled Plan'
AND PhysicalOperator IN ('nothing will ever match this one!'
--,'Assert'
--,'Bitmap'
--,'Clustered Index Delete'
--,'Clustered Index Insert'
,'Clustered Index Scan'
--,'Clustered Index Seek'
--,'Clustered Index Update'
--,'Compute Scalar'
--,'Concatenation'
--,'Constant Scan'
,'Deleted Scan'
--,'Filter'
--,'Hash Match'
,'Index Scan'
--,'Index Seek'
--,'Index Spool'
,'Inserted Scan'
--,'Merge Join'
--,'Nested Loops'
--,'Parallelism'
,'Parameter Table Scan'
--,'RID Lookup'
--,'Segment'
--,'Sequence Project'
--,'Sort'
--,'Stream Aggregate'
--,'Table Delete'
--,'Table Insert'
,'Table Scan'
--,'Table Spool'
--,'Table Update'
--,'Table-valued function'
--,'Top'
)
just add an ORDER BY on something like the combination of the UseCounts and EstimatedCost.
Here is what finally worked:
with xmlnamespaces(default 'http://schemas.microsoft.com/sqlserver/2004/07/showplan') , CachedPlans as (
select
query_plan,
n.value('../../../#StatementText' ,'varchar(1000)') as [Statement],
n.value('../../../#StatementSubTreeCost' ,'varchar(1000)') as [Cost],
n.value('../../../#StatementEstRows' ,'varchar(1000)') as [Rows],
n.value('#Impact' ,'float') as Impact,
n.value('MissingIndex[1]/#Database' ,'varchar(128)') as [Database],
n.value('MissingIndex[1]/#Table' ,'varchar(128)') as [TableName],
(
select dbo.concat(c.value('#Name' ,'varchar(128)'))
from n.nodes('MissingIndex/ColumnGroup[#Usage="EQUALITY"][1]') as t(cg)
cross apply cg.nodes('Column') as r(c)
) as equality_columns,
(
select dbo.concat(c.value('#Name' ,'varchar(128)'))
from n.nodes('MissingIndex/ColumnGroup[#Usage="INEQUALITY"][1]') as t(cg)
cross apply cg.nodes('Column') as r(c)
) as inequality_columns,
(
select dbo.concat(c.value('#Name' ,'varchar(128)'))
from n.nodes('MissingIndex/ColumnGroup[#Usage="INCLUDE"][1]') as t(cg)
cross apply cg.nodes('Column') as r(c)
) as include_columns
from (
select query_plan
from sys.dm_exec_cached_plans p
outer apply sys.dm_exec_query_plan(p.plan_handle) tp
) as tab(query_plan)
cross apply query_plan.nodes('//MissingIndexGroup') as q(n)
)
select *
from CachedPlans
You could run a profiler trace and check out the procedures that are running and their effectiveness in terms on index seeks / usage.
Rather than just do all indices for everyone, it is better to optimize the biggest problem - you usually will get the most benefit from this.
In the profiler trace, figure out which stored proc / tsql statement runs the most number of times and consumes the most resources. Those are the ones that you really want to go after.

Resources