patternsqlMinor
Does WITH SCHEMABINDING on a multi-statement TVF improve cardinality estimates?
Viewed 0 times
estimatesmulticardinalitystatementwithtvfimprovedoesschemabinding
Problem
Based on Query Performance and multi-statement table valued functions and other articles, SQL Server assumes that a multi-line table valued function returns one row. This causes the selection of a poor execution plan for the calling statement if it actually returns many rows.
Does adding
If we assume that we are passing a UserId to this function and getting back a table of RecordId values that the user is allowed to access, and that some users are only allowed to see a few records and that some are allowed to see many or even all records, would either the function or the calling statements (or the procedures that include them) benefit from using
I realize that I could figure this out by experimentation, but I am hoping that someone has already figured out the answer. A pointer to someplace where this is well documented would be helpful.
Does adding
SCHEMABINDING to the function result in a more correct cardinality estimate for the returned value set of the function?If we assume that we are passing a UserId to this function and getting back a table of RecordId values that the user is allowed to access, and that some users are only allowed to see a few records and that some are allowed to see many or even all records, would either the function or the calling statements (or the procedures that include them) benefit from using
RECOMPILE? Does the use of SCHEMABINDING in the function change this answer?I realize that I could figure this out by experimentation, but I am hoping that someone has already figured out the answer. A pointer to someplace where this is well documented would be helpful.
Solution
In my tests, no, adding
Then two functions:
Comparing the actual plans, both show estimated rows = 1, actual rows = 2112 (this latter number may differ on your system depending on version/SP etc).
Comparing the speed:
Results:
So, does it matter much? Nope.
WITH SCHEMABINDING does not improve cardinality estimates. I created a simple table:CREATE TABLE dbo.myobjects(id INT PRIMARY KEY);
INSERT dbo.myobjects SELECT [object_id] FROM sys.all_objects;Then two functions:
CREATE FUNCTION dbo.noschemabinding(@UserID INT)
RETURNS @x TABLE (id INT)
AS
BEGIN
INSERT @x SELECT id FROM dbo.myobjects;
RETURN;
END
GO
CREATE FUNCTION dbo.withschemabinding(@UserID INT)
RETURNS @x TABLE (id INT)
WITH SCHEMABINDING
AS
BEGIN
INSERT @x SELECT id FROM dbo.myobjects;
RETURN;
END
GOComparing the actual plans, both show estimated rows = 1, actual rows = 2112 (this latter number may differ on your system depending on version/SP etc).
Comparing the speed:
SET NOCOUNT ON;
GO
SELECT SYSDATETIME();
GO
SELECT id INTO #x FROM dbo.noschemabinding(1);
DROP TABLE #x;
GO 1000
GO
SELECT SYSDATETIME();
GO
SELECT id INTO #x FROM dbo.withschemabinding(1);
DROP TABLE #x;
GO 1000
SELECT SYSDATETIME();Results:
run 1 run 2
---------------- ------------------ ------------------
No schemabinding 14632 milliseconds 14079 milliseconds
Schemabinding 14251 milliseconds 13979 millisecondsSo, does it matter much? Nope.
SCHEMABINDING in this case is used for a more important goal: underlying schema stability. You will probably have much better optimization opportunities if you pursue converting your function to an inline TVF than to chase down obscure plan-affecting differences in a multi-statement TVF.Code Snippets
CREATE TABLE dbo.myobjects(id INT PRIMARY KEY);
INSERT dbo.myobjects SELECT [object_id] FROM sys.all_objects;CREATE FUNCTION dbo.noschemabinding(@UserID INT)
RETURNS @x TABLE (id INT)
AS
BEGIN
INSERT @x SELECT id FROM dbo.myobjects;
RETURN;
END
GO
CREATE FUNCTION dbo.withschemabinding(@UserID INT)
RETURNS @x TABLE (id INT)
WITH SCHEMABINDING
AS
BEGIN
INSERT @x SELECT id FROM dbo.myobjects;
RETURN;
END
GOSET NOCOUNT ON;
GO
SELECT SYSDATETIME();
GO
SELECT id INTO #x FROM dbo.noschemabinding(1);
DROP TABLE #x;
GO 1000
GO
SELECT SYSDATETIME();
GO
SELECT id INTO #x FROM dbo.withschemabinding(1);
DROP TABLE #x;
GO 1000
SELECT SYSDATETIME();run 1 run 2
---------------- ------------------ ------------------
No schemabinding 14632 milliseconds 14079 milliseconds
Schemabinding 14251 milliseconds 13979 millisecondsContext
StackExchange Database Administrators Q#40079, answer score: 5
Revisions (0)
No revisions yet.