Here's a sample variable-based table-valued Function:
create function getSamplePersonOrgsViaVariableBasedTableValuedFunction()
returns @t table
(
BusinessEntityID int primary key clustered,
OrgUnits nvarchar(max)
)
as
begin
insert into @t(BusinessEntityID, OrgUnits)
select p.BusinessEntityID, eorg.orgUnits
from Person.Person p
cross apply
(
select 'blah'
) eorg(orgUnits);
return;
end;
go
Here's a sample inline table-valued function:
create function getSamplePersonOrgsViaInlineTableValuedFunction() returns table
as
return
select p.BusinessEntityID, eorg.orgUnits
from Person.Person p
cross apply
(
select 'blah'
) eorg(orgUnits)
go
For 50 invocations of this sample query on inline table-valued function, this took 1 second only
SELECT p.BusinessEntityID, p.FirstName, p.LastName, gsp.orgUnits FROM Person.Person p join getSamplePersonOrgsViaInlineTableValuedFunction() gsp on p.BusinessEntityID = gsp.BusinessEntityID where p.BusinessEntityID < 9 go 50
Contrast that efficiency to variable-based table-valued function, variable-based table-valued function took 9 seconds:
SELECT p.BusinessEntityID, p.FirstName, p.LastName, gsp.orgUnits FROM Person.Person p join getSamplePersonOrgsViaVariableBasedTableValuedFunction() gsp on p.BusinessEntityID = gsp.BusinessEntityID where p.BusinessEntityID < 9 go 50
Eager loading is the root cause of the performance problem for variable-based table-valued function, i.e. even we only need 8 rows on source table (p.BusinessEntityID < 9), when the source table is joined to variable-based table-valued function, the source table has to wait first for the result (19,972 rows) of the variable-based table-valued function before it is finally being joined to.
Inline table-valued function is very efficient and smart, it treats the function like a view, i.e., the query of the inline table-valued function is expanded to an actual tables when being joined to another table, behaves exactly like the joining of a table to a table-deriving query / view / CTE. Hence when the inline table-valued function is joined to a source table, when you fetch 8 rows only on source table (p.BusinessEntityID < 9), the query will also fetch 8 rows only on inline-table-valued function too.
On variable-based table-valued function, the result of the function is the one being expanded then put to another table bucket (variable table), hence causing performance problem, so when for example we have 8 rows then we join it to a variable-based table-valued function, we are joining the 8 rows to eagerly-loaded 19,972 rows.
To illustrate the efficiency of inline table-valued function, let's cause a divide by zero when the record encounters Person ID number 9.
alter function getSamplePersonOrgsViaInlineTableValuedFunction() returns table
as
return
select p.BusinessEntityId, eorg.orgUnits
from dbo.Person.Person p
cross apply
(
select 'blah ' + convert(varchar,case when p.BusinessEntityId = 9 then 1 / 0 else 7 end)
) eorg(orgUnits)
go
alter function getSamplePersonOrgsViaVariableBasedTableValuedFunction()
returns @t table
(
BusinessEntityId int primary key clustered,
OrgUnits nvarchar(max)
)
as
begin
insert into @t(BusinessEntityId, OrgUnits)
select p.BusinessEntityId, eorg.orgUnits
from dbo.Person.Person p
cross apply
(
select 'blah ' + convert(varchar,case when p.BusinessEntityId = 9 then 1 / 0 else 7 end)
) eorg(orgUnits);
return;
end;
go
This query will not cause divide-by-zero error on inline table-valued function, a proof that the function doesn't fetch organization units after BusinessEntityID number 8
SELECT p.BusinessEntityID, p.FirstName, p.LastName, gsp.orgUnits FROM Person.Person p join getSamplePersonOrgsViaInlineTableValuedFunction() gsp on p.BusinessEntityID = gsp.BusinessEntityID where p.BusinessEntityID < 9
This query gets a divide-by-zero error though, a proof that the query fetch all the 19,972 rows of the variable-based table-valued function first before it is being joined to.
SELECT p.BusinessEntityID, p.FirstName, p.LastName, gsp.orgUnits FROM Person.Person p join getSamplePersonOrgsViaVariableBasedTableValuedFunction() gsp on p.BusinessEntityID = gsp.BusinessEntityID where p.BusinessEntityID < 9
Happy Computing! ツ
No comments:
Post a Comment