I needed to trim a number of strings in a select statement, so instead of repeating multiple ltrim(rtrim(' the string ') calls, I created a simple trim function as follows:
create function dbo.trim(#String varchar(max))
returns varchar(max)
as
begin
return rtrim(ltrim(#String))
end
go
However, during the execution of my select (which invokes the function multiple times) I run the following select in a different window as sa:
SELECT sqltext.TEXT,
req.session_id,
req.status,
req.command,
req.cpu_time,
req.total_elapsed_time
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext
and observe the following
TEXT session_id status command cpu_time total_elapsed_time
------------------------------------------------------------------
** 99 running SELECT 12045 12388
where the first column (replaced by ** above for readability) contains the following:
create function dbo.trim(#String varchar(max))
returns varchar(max)
as
begin
return rtrim(ltrim(#String))
end
Am I correctly interpreting this to mean that upon calling the trim function that SQL Server actually calls a create?
As you can see from the table above, the execution time is rather long and in some cases I've found the create operation to outright hang thus blocking the completion of the select and further write operations on the database such that I've had to kill the operation.
Can anyone shed some light on this?
Thanks!
Pab
Am I correctly interpreting this to mean that upon calling the trim
function that SQL Server actually calls a create?
No, SQL Server just shows you the full text of the SQL Module - which includes the CREATE. You can use statement_start_offset and statement_end_offset to see the statement actually being executed.
SideNote: You're better off not using scalar UDFs for something trivial like a TRIM function. These have well known performance problems. See Converting A Scalar User Defined Function To A Inline Table Valued Function for an alternative approach if this is worth encapsulating in a function at all.
Related
I'm facing a quite annoying barrier enforced by SQL Server and would like to check if there is an elegant solution for this.
I have a sequence of procedures' invocations (meaning, A calls B which calls C). The procedures are due to return different results sets, where (for instance) "A" generates its result using a set of records returned by "B".
Now, SQL Server does not allow to have nested INSERT INTO ... EXEC <stored procedure> so, to cope with this limitation, I converted the lowest procedure into a function that returns a table and hence INSERT INTO ... SELECT * FROM <function call>.
Now, there are situations in which the FUNCTION cannot return a result due to conditions of the data, and I would like the function to return a sort of code indicating the result of the execution (e.g. 0 would mean success, 1 would mean "missing input data").
Since SQL Server does not allow functions with OUTPUT parameters, I can't think of any elegant way of conveying these two outputs.
Can anyone suggest an elegant alternative?
there are situations in which the FUNCTION cannot return a result due
to conditions of the data, and I would like the function to return a
sort of code indicating the result of the execution
You really should use THROW to indicate the result of execution, which also precludes using a table-valued function.
So you need to use a stored procedure. To avoid the restriction on nested INSERT .. SELECT you can use temporary tables to pass data back to the calling procedure. EG
create or alter procedure foo
as
begin
if object_id('tempdb..#foo_results') is null
begin
print 'create table #foo_results(id int primary key, a int);';
THROW 51000, 'The results table #foo_results does not exist. Before calling this procedure create it. ', 1;
end
insert into #foo_results(id,a)
values (1,1);
end;
Can anyone suggest an ELEGANT alternative?
I'm not sure any of the alternatives is elegant.
When we use user defined functions in SQL to return a table, why don't we use BEGINand END?
e.g.
CREATE FUNCTION Customers
(#minId int)
RETURNS TABLE
AS
RETURN(SELECT *
FROM TrackingItem ti
WHERE ti.Id >= #minId)
works
but
CREATE FUNCTION Customers
(#minId int)
RETURNS TABLE
AS
BEGIN
RETURN(SELECT *
FROM TrackingItem ti
WHERE ti.Id >= #minId)
END
doesn't work
You did not tag the DBMS, but from your syntax I guess this is SQL Server...
Good approach: inline TVF
The inline syntax (without the BEGIN...END) works like a VIEW with parameters. On usage it will be fully inlined by the query optimizer - just as if the code was written in the place where the function is used (almost). This means: Full usage of indexes, statistics, chached results...
But - which is not possible in all cases (but in most cases) - you must be able to write your full logic in one single statement.
Bad approach: multi-statement TVF
The second example needs a table definition which fits to the result you want to return. Your code has to use an INSERT against this virtual table and then return it. This is missing in your example.
This approach is absolutely to be avoided if ever possible. The query optimizer will not be able to look into this code in order to predict the result. It will not be able to use interimistic / cached results or indexes / statistics in the way an inlined query would do it.
The first one, without the BEGIN and END is called an "Inline Table Valued Function". You can only have the single statement inside the function body to immediately return the resultset.
The second one with the BEGIN and END is called a "Multi Statement Table Valued Function". You can have multiple statements inside the function body and then at the bottom have a RETURN statement to return the resultset. This allows you to e.g. populate a TABLE variable and then return it.
An Inline Table Valued Function can be thought of like a view - in it gets expanded out into the calling query, statistics on the underlying tables can be used to give a better execution plan.
Multi Statement Valued Functions do not expand out like this, and don't benefit from the same benefits when an execution plan is created.
So, it's best practise to avoid multi statement table valued functions, and prefer inline instead, in order to reap these benefits and avoid potential performance issues.
You can use in this ways, you are returning table type information and you have to define table with columns and return data through the table: Try following suppose you have 2 columns id and name:
CREATE FUNCTION Customers(#minId INT)
RETURNS ##table TABLE (id INT, NAME VARCHAR(20))
AS
BEGIN
INSERT INTO ##table
SELECT id, name
FROM TrackingItem ti
WHERE ti.Id >= #minId
RETURN
END
I'm using Change Tracking in SQL2008 R2 and seeing a very odd behaviour when trying to determine rows affected in a batch, a stored proc takes ~30 seconds to run when using a parameter value, but when I put the literal values in the call to the CHANGETABLE function it returns in <1s.
A call to the following takes ~30s:
DECLARE #sync_last_received_anchor BigInt;
DECLARE #sync_new_received_anchor BigInt;
SET #sync_last_received_anchor = 272361;
SET #sync_new_received_anchor = 273361;
SELECT [Customer].[CustomerID]
FROM dbo.tblCustomer AS [Customer] WITH (NOLOCK)
INNER JOIN CHANGETABLE(CHANGES [REDACTED].[dbo].[tblCustomer], #sync_last_received_anchor) AS [theChangeTable]
ON [theChangeTable].[CustomerID] = [Customer].[CustomerID]
WHERE ([theChangeTable].[SYS_CHANGE_OPERATION] = 'U'
AND [theChangeTable].[SYS_CHANGE_VERSION] <= #sync_new_received_anchor
)
However changing the CHANGETABLE line, as below, reduces it to ~1s.
INNER JOIN CHANGETABLE(CHANGES [REDACTED].[dbo].[tblCustomer], 272361) AS [theChangeTable]
As we're running SP1 I presume the patch released in SQL2008 CU4 for CHANGETABLE being slow has been fixed (http://support.microsoft.com/kb/2276330).
I'm at a loss though why changing a parameter to a literal value would make so much difference?
It is likely that the stored procedure is doing parameter sniffing - i.e. it thinks the values you supplied are a good match for a plan it has already cached and it it isn't a good match at all.
There are multiple articles on how to approach this issue, one you can test and try on a DEV environment would be this:
http://blogs.msdn.com/b/axperf/archive/2010/05/07/important-sql-server-change-parameter-sniffing-and-plan-caching.aspx
I remember reading a while back that randomly SQL Server can slow down and / or take a stupidly long time to execute a stored procedure when it is written like:
CREATE PROCEDURE spMyExampleProc
(
#myParameterINT
)
AS
BEGIN
SELECT something FROM myTable WHERE myColumn = #myParameter
END
The way to fix this error is to do this:
CREATE PROCEDURE spMyExampleProc
(
#myParameterINT
)
AS
BEGIN
DECLARE #newParameter INT
SET #newParameter = #myParameter
SELECT something FROM myTable WHERE myColumn = #newParameter
END
Now my question is firstly is it bad practice to follow the second example for all my stored procedures? This seems like a bug that could be easily prevented with little work, but would there be any drawbacks to doing this and if so why?
When I read about this the problem was that the same proc would take varying times to execute depending on the value in the parameter, if anyone can tell me what this problem is called / why it occurs I would be really grateful, I cant seem to find the link to the post anywhere and it seems like a problem that could occur for our company.
The problem is "parameter sniffing" (SO Search)
The pattern with #newParameter is called "parameter masking" (also SO Search)
You could always use the this masking pattern but it isn't always needed. For example, a simple select by unique key, with no child tables or other filters should behave as expected every time.
Since SQL Server 2008, you can also use the OPTIMISE FOR UNKNOWN (SO). Also see Alternative to using local variables in a where clause and Experience with when to use OPTIMIZE FOR UNKNOWN
Is there a way to call a User defined function without using "dbo." before the function name and parameters?
Using:
SELECT USERFUNCTION(PARAM1, PARAM2, PARAM3, PARAMN)
instead of:
SELECT dbo.USERFUNCTION(PARAM1, PARAM2, PARAM3, PARAMN)
This isn't possible for the SELECT syntax. BOL States: "Scalar-valued functions must be invoked by using at least the two-part name of the function"
This syntax works however.
CREATE FUNCTION USERFUNCTION
(#p INT)
RETURNS INT
AS
BEGIN
RETURN (2)
END
GO
DECLARE #rc INT
EXEC #rc = USERFUNCTION 1
SELECT #rc
It is best practice to always explicitly schema qualify objects you are referencing anyway though to avoid some overhead for resolving the schema (and avoid the possibility that the schema cannot be resolved implicitly or is resolved in a way that is undesired)
There are various ways to do this, if we take it that you have a negative reaction to seeing "dbo.".
In SQL Server 2000, there is a way to turn UDFs into system functions by toggling a bit. This "feature" has been removed from SQL Server 2005 onwards, so I won't go into detail unless you really are still using 2000.
You can use OPENQUERY with PROC syntax similar to what Martin has shown.
You can turn the Scalar function into a Table Valued Function, either by rewriting it, or by wrapping it in a TVF. The syntax changes however, so
select dbo.udf(a,b) from c
--becomes
select d
from c
cross apply tvf(a,b) e(d) -- look, no "dbo"!
But none of the above looks simpler than just tacking a simple "dbo." prefix to the function name, so why would you do it?
Yes Possible,
Actually when function returning scalar value you must call with schema name like dbo.yourfunction , If you don't want to call function without schema name you should create function as follows.
Sample Code:
CREATE FUNCTION [dbo].[FN_MAPCOUNT]
(
#countValue int
)
RETURNS #TEMPTABLE Table(cntValue int)
as
begin
DECLARE #countValueint
#countValue= select count(*) from mappings;
INSERT #TEMPTABLE (cntValue) VALUES (#countValue)
RETURN
end
Go
select * from FN_MAPCOUNT(1);
The reason is you are returning the value as table .