Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 Performance: UDF, Stored Proc, or Inline SQL?

Author  Topic 

ferrethouse
Constraint Violating Yak Guru

352 Posts

Posted - 2011-10-25 : 15:28:31
Currently my application calls a function called "FuncDepartmentChildren" about 5 million times per hour. I have been investigating tempdb performance and found that this query causes the largest stress on tempdb of all queries. The function itself is dead simple so I'm thinking it would be better to use a stored proc or just call the SELECT statement directly inline so that writes to tempdb wouldn't be needed. Here is the function. Advice appreciated...


USE [absorb]
GO
/****** Object: UserDefinedFunction [dbo].[FuncDepartmentChildren] Script Date: 10/25/2011 13:21:38 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER FUNCTION [dbo].[FuncDepartmentChildren] (@DeptID int, @Inclusive int)
RETURNS @DepartmentIDs TABLE
(
DepartmentID int
)

AS
BEGIN
IF (@DeptID > 0)
BEGIN
-- Insert the base department if flagged
IF @Inclusive = 1
BEGIN
INSERT INTO @DepartmentIDs VALUES (@DeptID)
END

INSERT INTO @DepartmentIds SELECT ChildDepartmentID FROM DepartmentChildren WHERE DepartmentID = @DeptID
END
RETURN
END

robvolk
Most Valuable Yak

15732 Posts

Posted - 2011-10-25 : 18:02:50
This version may provide better performance since it's an inline table:
ALTER FUNCTION [dbo].[FuncDepartmentChildren] (@DeptID int, @Inclusive int)
RETURNS TABLE AS
RETURN( SELECT ChildDepartmentID FROM DepartmentChildren
WHERE @DeptID>0 AND DepartmentID = @DeptID
UNION ALL
SELECT DeptID FROM (SELECT @DeptID DeptID) a
WHERE DeptID>0 AND @Inclusive=1)
You'll need to test it to make sure it returns the same data for the same parameters. If you prefer to inline it in a procedure, the SELECT would look more or less the same, just remove the function definition keywords.
Go to Top of Page

ferrethouse
Constraint Violating Yak Guru

352 Posts

Posted - 2011-10-26 : 01:35:24
Cool. Do you know if doing it inline causes it not to write to tempdb?
Go to Top of Page

Transact Charlie
Master Smack Fu Yak Hacker

3451 Posts

Posted - 2011-10-26 : 05:58:47
http://stackoverflow.com/questions/2554333/multi-statement-table-valued-function-vs-inline-table-valued-function

An inline implementation should be treated as if it were a view so I'd expect it not to write to tempdb unless the parent query needs to use a worktable.



Charlie
===============================================================
Msg 3903, Level 16, State 1, Line 1736
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION
Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 08:02:03
quote:
Originally posted by Transact Charlie

http://stackoverflow.com/questions/2554333/multi-statement-table-valued-function-vs-inline-table-valued-function

An inline implementation should be treated as if it were a view so I'd expect it not to write to tempdb unless the parent query needs to use a worktable.



Charlie
===============================================================
Msg 3903, Level 16, State 1, Line 1736
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION




The link you have posted has some ridiculous claims.

An inline table valued function is treated as a view..agreed but a plan reuse for an inline function will only occur when the main statement calling the inline function is executed with the same set of parameters.Also a multi valued function is unable to use the statistics on the tables which it is querying.

So the answer is not as simple as either its black or its white and that always the MVF are the bad guys.The answer is..it depends..

If you have columns which do not get updated frequently and cardinality is low then go for multi valued function and if that's not the case then use a inline valued function.But beware of the trade off which I had mentioned earlier.


PBUH

Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 08:07:28
quote:
Originally posted by ferrethouse

Currently my application calls a function called "FuncDepartmentChildren" about 5 million times per hour. I have been investigating tempdb performance and found that this query causes the largest stress on tempdb of all queries. The function itself is dead simple so I'm thinking it would be better to use a stored proc or just call the SELECT statement directly inline so that writes to tempdb wouldn't be needed. Here is the function. Advice appreciated...


USE [absorb]
GO
/****** Object: UserDefinedFunction [dbo].[FuncDepartmentChildren] Script Date: 10/25/2011 13:21:38 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER FUNCTION [dbo].[FuncDepartmentChildren] (@DeptID int, @Inclusive int)
RETURNS @DepartmentIDs TABLE
(
DepartmentID int
)

AS
BEGIN
IF (@DeptID > 0)
BEGIN
-- Insert the base department if flagged
IF @Inclusive = 1
BEGIN
INSERT INTO @DepartmentIDs VALUES (@DeptID)
END

INSERT INTO @DepartmentIds SELECT ChildDepartmentID FROM DepartmentChildren WHERE DepartmentID = @DeptID
END
RETURN
END




Just not sure why do you need a function at all at the first place.A temp table would simply suffice your requirement.

Create a temp table,add an index on childdeptid in the temp table,load the temp table joining all the related tables and then join the temp table in the main query with the other related tables based on childdeptid.

PBUH

Go to Top of Page

Lamprey
Master Smack Fu Yak Hacker

4614 Posts

Posted - 2011-10-26 : 12:28:31
quote:
Originally posted by Sachin.Nand

<snip>but a plan reuse for an inline function will only occur when the main statement calling the inline function is executed with the same set of parameters<snip>
Do you have any links to show that is true?

quote:

So the answer is not as simple as either its black or its white and that always the MVF are the bad guys.The answer is..it depends..

Agreed. I don't see how anyone can possibly answer the OPs origianl question as we don't know in what context the funtion is being used.
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2011-10-26 : 13:43:46
How and WHY is this Function being called?

betcha you don't need a function

Brett

8-)

Hint: Want your questions answered fast? Follow the direction in this link
http://weblogs.sqlteam.com/brettk/archive/2005/05/25/5276.aspx


Want to help yourself?

http://msdn.microsoft.com/en-us/library/ms130214.aspx

http://weblogs.sqlteam.com/brettk/

http://brettkaiser.blogspot.com/


Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 13:51:25
quote:
Originally posted by Lamprey

quote:
Originally posted by Sachin.Nand

<snip>but a plan reuse for an inline function will only occur when the main statement calling the inline function is executed with the same set of parameters<snip>
Do you have any links to show that is true?



Why do you need a link ? I myself will prove it with an example.It is not something theoretical or academic and untried and posted from some link.I myself have found this.

In the first query window run this statement.DO NOT try this on any prod server as I am using dbcc freeprocache to flush the buffers

Use AdventureWorks
go

create function fn_inline(@ProductId int)
returns table
as
return
select WorkOrderID,OrderQty from Production.WorkOrder where ProductID=@ProductId

go
create function fn_multivalued(@ProductId int)
returns @t table(WorkOrderID int,OrderQty int)
as begin

insert @t
select WorkOrderID,OrderQty from Production.WorkOrder where ProductID=@ProductId

return
end
go

dbcc freeproccache
go


Now in second query window run this

Use AdventureWorks
go
select * from fn_inline(722)

select * from fn_multivalued(722)

And in the third query window run this

Use AdventureWorks
go
select usecounts,text,cacheobjtype,objtype from sys.dm_exec_cached_plans
cross apply sys.dm_exec_sql_text(plan_handle)
where text like '%function fn_%'

Now if you see the output of the third query window you will see that the plan for the inline function is never reused but the usecounts for the multivalued function increases for each subsequent execution of the function.

Try changing the parameter of the functions to 725 still you will see that the usecounts for MVF increases which indicates plan reuse irrespective of the parameters passed and there is never different plan created for the same function.The column cacheobjtype returned by the above query indicates MVF to be of the type Compiled plan while that of Inline function is not.

Also the query cost in the execution plan of the inline function is 99% as compared to the MVF which is 1% which again indicates that the plan compile happening for the inline function.

I hope the above explanation has answered your question


PBUH

Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 13:54:26
quote:
Originally posted by Lamprey
Agreed. I don't see how anyone can possibly answer the OPs origianl question as we don't know in what context the funtion is being used.



Well that's the reason I did suggest the OP to overall get rid of the function itself as he was facing performance issue with it and instead use a temp table.

PBUH

Go to Top of Page

Lamprey
Master Smack Fu Yak Hacker

4614 Posts

Posted - 2011-10-26 : 14:08:13
I don't get the same resutls as you. I see the inline function being reused almost every time. When I say almost I mean 20 out of 25 times. I tried a combination of literal values and variables (assigning a variable and passing that). So, I don't think you statement that "plan reuse for an inline function will only occur when the main statement calling the inline function is executed with the same set of parameters" is 100% true.
Go to Top of Page

Lamprey
Master Smack Fu Yak Hacker

4614 Posts

Posted - 2011-10-26 : 14:12:48
quote:
Originally posted by Sachin.Nand

quote:
Originally posted by Lamprey
Agreed. I don't see how anyone can possibly answer the OPs origianl question as we don't know in what context the funtion is being used.



Well that's the reason I did suggest the OP to overall get rid of the function itself as he was facing performance issue with it and instead use a temp table.

PBUH



How does a temp table solve the issue? BTW, That's a rhetorical question. We don't know how the function is being used, thus we can't say it will solve the issue. It's possible that the function is being used as part of an OUTER APPLY or something on a large table. We don't know. A temp table may well be the solution. But, until the OP gives more context it's impossible to offer anything but a shot in the dark.
Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 14:17:23
quote:
Originally posted by Lamprey

I don't get the same resutls as you. I see the inline function being reused almost every time. When I say almost I mean 20 out of 25 times. I tried a combination of literal values and variables (assigning a variable and passing that). So, I don't think you statement that "plan reuse for an inline function will only occur when the main statement calling the inline function is executed with the same set of parameters" is 100% true.



Whats the cacheobjtype you are getting for the inline function ?

PBUH

Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 14:27:40
quote:
Originally posted by Lamprey

quote:
Originally posted by Sachin.Nand

quote:
Originally posted by Lamprey
Agreed. I don't see how anyone can possibly answer the OPs origianl question as we don't know in what context the funtion is being used.



Well that's the reason I did suggest the OP to overall get rid of the function itself as he was facing performance issue with it and instead use a temp table.

PBUH



How does a temp table solve the issue? BTW, That's a rhetorical question. We don't know how the function is being used, thus we can't say it will solve the issue. It's possible that the function is being used as part of an OUTER APPLY or something on a large table. We don't know. A temp table may well be the solution. But, until the OP gives more context it's impossible to offer anything but a shot in the dark.



Oh please a function with an OUTER APPLY...

A Inline function is very much part of the main query just like views but MVF is always evaluated in a separate context from the main query and the results of MVF are evaluated in temp work tables..Index spools..

The whole separate context and worktables make MVF very costly.If you combine a MVF with a APPLY clause sql server will execute the MVF and populate the worktable multiple times once for each row generated by the apply clause.

Makes sense ??


PBUH

Go to Top of Page

Lamprey
Master Smack Fu Yak Hacker

4614 Posts

Posted - 2011-10-26 : 14:33:00
quote:
Originally posted by Sachin.Nand

quote:
Originally posted by Lamprey

I don't get the same resutls as you. I see the inline function being reused almost every time. When I say almost I mean 20 out of 25 times. I tried a combination of literal values and variables (assigning a variable and passing that). So, I don't think you statement that "plan reuse for an inline function will only occur when the main statement calling the inline function is executed with the same set of parameters" is 100% true.



Whats the cacheobjtype you are getting for the inline function ?

PBUH



I'm getting a Parse Tree.
Go to Top of Page

Lamprey
Master Smack Fu Yak Hacker

4614 Posts

Posted - 2011-10-26 : 14:35:46
quote:
Originally posted by Sachin.Nand

Oh please a function with an OUTER APPLY...

A Inline function is very much part of the main query just like views but MVF is always evaluated in a separate context from the main query and the results of MVF are evaluated in temp work tables..Index spools..

The whole separate context and worktables make MVF very costly.If you combine a MVF with a APPLY clause sql server will execute the MVF and populate the worktable multiple times once for each row generated by the apply clause.

Makes sense ??


PBUH



Are you saying people don't use functions in outer applys or do you think its a silly thing to do? I think it's silly, but I wouldn't dismiss people from doing anything.

I get how functions work. My only point is that speculating on a solution is pointless since we don't know how the function is used. Nothing more. Nothing less.
Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 14:36:55
quote:
Originally posted by Lamprey

quote:
Originally posted by Sachin.Nand

quote:
Originally posted by Lamprey

I don't get the same resutls as you. I see the inline function being reused almost every time. When I say almost I mean 20 out of 25 times. I tried a combination of literal values and variables (assigning a variable and passing that). So, I don't think you statement that "plan reuse for an inline function will only occur when the main statement calling the inline function is executed with the same set of parameters" is 100% true.



Whats the cacheobjtype you are getting for the inline function ?

PBUH



I'm getting a Parse Tree.



A parse tree <> A compiled plan..

PBUH

Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-26 : 14:45:34
quote:
Originally posted by Lamprey

quote:
Originally posted by Sachin.Nand

Oh please a function with an OUTER APPLY...

A Inline function is very much part of the main query just like views but MVF is always evaluated in a separate context from the main query and the results of MVF are evaluated in temp work tables..Index spools..

The whole separate context and worktables make MVF very costly.If you combine a MVF with a APPLY clause sql server will execute the MVF and populate the worktable multiple times once for each row generated by the apply clause.

Makes sense ??


PBUH



Are you saying people don't use functions in outer applys or do you think its a silly thing to do? I think it's silly, but I wouldn't dismiss people from doing anything.

I get how functions work. My only point is that speculating on a solution is pointless since we don't know how the function is used. Nothing more. Nothing less.



I am not saying people don't use functions in apply clause.Yes they do.. and when the data gets huge they come up on the forums posting performance issue..

My whole point here is how a MVF will behave in case used with an APPLY clause and I am not speculating on a solution here.

The OP has posted his function.Its so simple on how a this function can easily be substituted by a temp table in a SP and achieve the desired result..

PBUH

Go to Top of Page

ferrethouse
Constraint Violating Yak Guru

352 Posts

Posted - 2011-10-27 : 12:46:09
This is how it is being used. It is linq code (sorry)...


var departments = from d in db.FuncDepartmentChildren(NetGrid.DepartmentID, 1) select d.DepartmentID;

var report = from t in db.Tasks
where t.ClientID == NetGrid.ClientIDLMS
&& (NetGrid.DepartmentID == 0 || NetGrid.DepartmentSelect != 0 || t.DepartmentID == NetGrid.DepartmentID)
&& (NetGrid.DepartmentID == 0 || NetGrid.DepartmentSelect != 2 || t.Department.ParentDeptID == NetGrid.DepartmentID)
&& (NetGrid.DepartmentID == 0 || NetGrid.DepartmentSelect != 1 || departments.Contains(t.DepartmentID))
select new
{
ID = t.TaskID,
Category = t.TaskCategory,
Task = t.TaskName,
Description = t.TaskDescription,
Department = t.Department.DepartmentName,
Steps = t.TaskSteps.Count(),
RiskLevel = db.DescTypes.Where(d => d.TypeGroup == TypeGroups.Hazard
&& Convert.ToInt32(d.Type) <= (t.TaskRatingProbability * t.TaskRatingExposure * t.TaskRatingSeverity)
&& Convert.ToInt32(d.TypeRange) >= (t.TaskRatingProbability * t.TaskRatingExposure * t.TaskRatingSeverity)).FirstOrDefault().TypeDesc,
RiskRating = (t.TaskRatingProbability * t.TaskRatingExposure * t.TaskRatingSeverity),
Added = t.TaskAdded,
Reviewed = t.TaskEdited,
};

return report;

Go to Top of Page

Sachin.Nand

2937 Posts

Posted - 2011-10-27 : 15:13:22
GREAT...Now we are back to square one.Why do you need a function in the first place.It could had easily been achieved by this


var childdepartments= from t in db.DepartmentChildren where t.inclusive=1;select t.DepartmentID;
var departments = from d in db.Departments; select d.DepartmentID;
var DepartmentChildren=from dept in departments from childdpt in childdepartments where dept.DepartmentID==childdpt.DepartmentID



PBUH

Go to Top of Page

ferrethouse
Constraint Violating Yak Guru

352 Posts

Posted - 2011-10-28 : 18:08:07
quote:
Originally posted by Sachin.Nand

GREAT...Now we are back to square one.Why do you need a function in the first place.It could had easily been achieved by this


var childdepartments= from t in db.DepartmentChildren where t.inclusive=1;select t.DepartmentID;
var departments = from d in db.Departments; select d.DepartmentID;
var DepartmentChildren=from dept in departments from childdpt in childdepartments where dept.DepartmentID==childdpt.DepartmentID



PBUH





That's great. I'll give that a try.

Incidentally, running it inline as rob suggested above had a HUGE beneficial impact on performance. So thanks for that rob.
Go to Top of Page
    Next Page

- Advertisement -