DECLARE @T TABLE
(
ID BIGINT IDENTITY PRIMARY KEY,
FaceBookID BIGINT NULL,
TwitterID BIGINT NULL,
LinkedInID VARCHAR(50) NULL
);
INSERT INTO @T (FaceBookID, TwitterID, LinkedInID)
VALUES (11111111, NULL, NULL)
INSERT INTO @T (FaceBookID, TwitterID, LinkedInID)
VALUES (NULL, 22222222, NULL)
INSERT INTO @T (FaceBookID, TwitterID, LinkedInID)
VALUES (NULL, NULL, '3333333')
DECLARE @UserType VARCHAR(10)
SET @UserType = 'LinkedIn'
DECLARE @oAuthID VARCHAR(50)
SET @oAuthID = 'aaaaaaa'
DECLARE @UserID BIGINT
SELECT @UserID = (
SELECT ID FROM @T
WHERE (@UserType = 'FaceBook' AND [FaceBookID] = CAST(@oAuthID AS BIGINT))
OR (@UserType = 'Twitter' AND [TwitterID] = CAST(@oAuthID AS BIGINT))
OR (@UserType = 'LinkedIn' AND [LinkedInID] = @oAuthID)
)
SELECT @UserID
PROBLEM:
Even when the UserType is 'LinkedIn' the first WHERE clause gets executed completely and the SQL tries to CAST the value of @oAuthID, which in this case is 'aaaaaaa', into a BIGINT.
QUESTION:
How can I write a WHERE CLAUSE where IF the first part is NOT TRUE, the second one is NOT executed?
Ideally, because @UserType is NOT 'FaceBook', we should NOT try to calculate the second part of that line (the CAST).
While sql does do short circuiting (the feature that allows a language to only evaluate one part of a condition), it does not do it in the same way as other languages.
Sql Server uses an optimizer to build an execution plan for a query. The optimizer looks at the query, and tries to build it in the most efficient way possible. Most of the time it optimizes for the estimated number of results or best index use: it wants to keep fewer records locked at a time and fewer records in memory at a time. But when sets from two conditions are about the same size or match similar indexes, it can also mean things like checking indexed columns in the where clause before non-indexed columns, or making small (and therefore fast) comparisons like bits or integers before longer (and therefore slower) comparisons (like string matches).
In this case, your best chance is to treat both sides as a text type (varchar, nchar, etc) and use that for your match. It will be slower, but you must always code for correctness first, and performance second.
This should work:
WHERE @oAuthID = CASE WHEN @UserType = 'Facebook' THEN Cast(FaceBookID as varchar(50))
WHEN @UserType = 'Twitter' THEN Cast(TwitterID as varchar(50))
WHEN @UserType = 'LinkedIn' THEN Cast(LinkedInID as varchar(50))
ELSE 'invalid user type' /* could use NULL here - as long as you'll never actually see a query with this value */
END
Change your query to this:
SELECT @UserID = (
SELECT ID FROM @T
WHERE (@UserType = 'FaceBook' AND [FaceBookID] = CASE WHEN ISNUMERIC(@oAuthID) = 1 THEN CAST(@oAuthID AS BIGINT) ELSE NULL END)
OR (@UserType = 'Twitter' AND [TwitterID] = CASE WHEN ISNUMERIC(@oAuthID) = 1 THEN CAST(@oAuthID AS BIGINT) ELSE NULL END)
OR (@UserType = 'LinkedIn' AND [LinkedInID] = @oAuthID)
)
Notice, where I put null on the else leg, assign the default of your choice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With