Reputation: 2397
This is in SQL Server 2008 R2.
I'm pulling my hair with this one. Follow closely, please.
When I run this, I get 27 rows returned:
select *
from dbo.[12STD_NO_VISIT]
where (
(dbo.fPhoneExists(PhoneNumber1) = 1
AND (NextCall1 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
And when I run this, I get 21 rows returned (notice the change to PhoneNumber2 and NextCall2):
select *
from dbo.[12STD_NO_VISIT]
where (
(dbo.fPhoneExists(PhoneNumber2) = 1
AND (NextCall2 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
But, when I run this, 'ORing' the 2 conditions, I get an error:
Conversion failed when converting the varchar value 'N' to data type int
select *
from dbo.[12STD_NO_VISIT]
where (
(dbo.fPhoneExists(PhoneNumber1) = 1
AND (NextCall1 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
OR
(dbo.fPhoneExists(PhoneNumber2) = 1
AND (NextCall2 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
But it doesn't just give me the error. It first retrieves 42 rows, displaying that for a split second (Results tab), and then it displays the error (Messages tab).
I can't figure this one out. Any help is very appreciated.
Thanks!
FUNCTION [dbo].[fPhoneExists](@PhoneNumber varchar)
RETURNS BIT
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE @GoodNumber bit
IF (@PhoneNumber is NULL or @PhoneNumber = 0 or @PhoneNumber = '')
SET @GoodNumber = 0;
ELSE
SET @GoodNumber = 1;
Return(@GoodNumber);
END
Upvotes: 2
Views: 18579
Reputation: 7093
You'll get better performance replacing fnPhoneExists
by replacing the function with a CASE
:
select *
from dbo.[12STD_NO_VISIT]
where (
(case when ISNULL(PhoneNumber1,'') not in ('0','') then 1 else 0 end=1
AND (NextCall1 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
OR
(case when ISNULL(PhoneNumber2,'') not in ('0','') then 1 else 0 end=1
AND (NextCall2 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
This is because the optimizer won't optimize the contents of fnPhoneExists.
Upvotes: 1
Reputation:
What are the data types of PhoneNumber1
and PhoneNumber2
? What is the definition of dbo.fPhoneExists
? I suspect the problem lies there somewhere - or as Lynn suggests maybe there is more to the query than you've shown us (the query would either succeed or fail as a whole; it wouldn't produce 42 rows and then an error).
Now that we see the function, here is a re-write:
ALTER FUNCTION [dbo].[fPhoneExists]
(
@PhoneNumber VARCHAR -- varchar(what)? This should match definition of column
)
RETURNS BIT
WITH EXECUTE AS CALLER
AS
BEGIN
RETURN (SELECT CASE WHEN COALESCE(@PhoneNumber, '') IN ('0', '') THEN 0 ELSE 1 END);
END
GO
There is no reason to store the temporary logic in a variable, also notice that you are returning the variable only in the ELSE condition.
Upvotes: 4
Reputation: 294287
Your dbo.fPhoneExists
function contains an implicit cast in the PhoneNumber = 0
expression that, according to the rules of Data Type Peecendence casts the PhoneNumber
VARCHAR value to an int. This cast will fail if the value in the string is not a numeric. You are also falling into the fallacy of assuming that boolean operator short circuit is guaranteed in SQL, which is simply not true. SQL is a declarative language and the order of evaluation of boolean operators is not guaranteed.
Upvotes: 3