Reputation: 85
I have this following select query that uses a scalar function to get full name. I want to eliminate the redundancy by using variable but so far there is no success. My query is
select
a.Id,
a.UserName,
getFullName(a.UserName),
a.CreateTime
from DataTable;
I don't want to retrieve 'a.User' two times. I would prefer if I can save a.User in a variable and then pass it to the function hence improving the efficiency.
Currently the work around I came up with is as following
select
Id,
UserName,
getFullName(UserName),
CreateTime
from (select a.Id, a.UserName, a.CreateTime from DataTable) temp
This solves the performance issue but adds the overhead to write same select two time. Any other suggestions would be great.
DataTable looks like this
+----+----------+------------+
| Id | UserName | CreateTime |
+----+----------+------------+
| 1 | ab | 10:00 |
| 2 | cd | 11:00 |
| 3 | ef | 12:00 |
+----+----------+------------+
Here is the NamesTable used to get the full names
+----------+----------+
| UserName | FullName |
+----------+----------+
| ab | Aa BB |
| cd | Cc Dd |
| ef | Ee Ff |
+----------+----------+
Here is the function that gets the full name
Create function [dbo].[getFullName](@user varchar(150)) returns varchar(500)
as
begin
declare @Result varchar(500);
select @Result = FullName from dbo.NamesTable where UserName = @user;
return @Result;
end;
Upvotes: 3
Views: 159
Reputation: 28900
Scalar UDF will execute for every row,but not defintely the way you think.below is sample demo and execution plan which proves the same..
create table testid
(
id int,
name varchar(20)
)
insert into testid
select n,'abc'
from numbers
where n<=1000000
create index nci_get on dbo.testid(id,name)
select id,name,dbo.getusername(id) from dbo.testid where id>4
below is the execution plan for above query
Decoding above plan:
Index seek outputs id,name
Then compute scalar tries to calculate new rows from existing row values.in this case expr1003 which is our function
Index seek cost is 97%,compute scalar cost is 3% and as you might be aware index seek is not an operator which goes to table to get data.so hopefully this clears your question
Upvotes: 2
Reputation: 47464
As D Stanley says, you're trying to solve some problem that doesn't exist. I would further add that you shouldn't be using the function at all. SQL is meant to perform set-based operations. When you use a function like that you're now making it perform the same function over and over again for every row - a horrible practice. Instead, just JOIN
in the other table (a set-based operation) and let SQL do what it does best:
SELECT
DT.Id,
DT.UserName,
NT.fullname,
DT.CreateTime
FROM
DataTable DT
INNER JOIN NamesTable NT ON NT.username = DT.username;
Also, DataTable
and NamesTable
are terrible names for tables. Of course they're tables, so there's no need to put "table" on the end of the name. Further, of course the first one holds "data", it's a database. Your table names should be descriptive. What exactly does DataTable
hold?
If you're going to be doing SQL development in the future then I strongly suggest that you read several introductory books on the subject and watch as many tutorial videos as you can find.
Upvotes: 3
Reputation: 152556
You're solving a problem that doesn't exist. You seem to think that
select
a.Id,
a.UserName,
getFullName(a.UserName),
a.CreateTime
from DataTable;
Has some relatively expensive process behind it to get UserName
that is happening twice. In reality, once the record is located, getting the UserName
value is an virtually instant process since it will probably be stored in a "variable" by the SQL engine behind the scenes. You should have little to no performance difference between that query and
select
a.Id,
getFullName(a.UserName),
a.CreateTime
from DataTable;
The scalar function itself may have a performance issue, but it's not because you are "pulling" the UserName
value "twice".
A better method would be to join to the other table:
select
a.Id,
a.UserName,
b.FullName,
a.CreateTime
from DataTable a
LEFT JOIN dbo.NamesTable b
ON a.UserName = b.UserName
Upvotes: 4