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 ONGOSET QUOTED_IDENTIFIER ONGOALTER FUNCTION [dbo].[FuncDepartmentChildren] (@DeptID int, @Inclusive int)RETURNS @DepartmentIDs TABLE ( DepartmentID int )ASBEGIN 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 RETURNEND |
|
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 ASRETURN( 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. |
 |
|
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? |
 |
|
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-functionAn 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 1736The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION |
 |
|
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-functionAn 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 1736The 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 |
 |
|
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 ONGOSET QUOTED_IDENTIFIER ONGOALTER FUNCTION [dbo].[FuncDepartmentChildren] (@DeptID int, @Inclusive int)RETURNS @DepartmentIDs TABLE ( DepartmentID int )ASBEGIN 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 RETURNEND
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 |
 |
|
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. |
 |
|
X002548
Not Just a Number
15586 Posts |
|
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 buffersUse AdventureWorksgocreate function fn_inline(@ProductId int)returns tableasreturnselect WorkOrderID,OrderQty from Production.WorkOrder where ProductID=@ProductIdgocreate function fn_multivalued(@ProductId int)returns @t table(WorkOrderID int,OrderQty int)as begininsert @tselect WorkOrderID,OrderQty from Production.WorkOrder where ProductID=@ProductIdreturnendgodbcc freeproccachego Now in second query window run thisUse AdventureWorksgoselect * from fn_inline(722)select * from fn_multivalued(722) And in the third query window run thisUse AdventureWorksgoselect 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 questionPBUH |
 |
|
Sachin.Nand
2937 Posts |
Posted - 2011-10-26 : 13:54:26
|
quote: Originally posted by LampreyAgreed. 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 |
 |
|
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. |
 |
|
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 LampreyAgreed. 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. |
 |
|
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 |
 |
|
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 LampreyAgreed. 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 |
 |
|
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. |
 |
|
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. |
 |
|
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 |
 |
|
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 |
 |
|
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; |
 |
|
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 thisvar 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 |
 |
|
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 thisvar 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. |
 |
|
Next Page
|