1

I have a simple query that I am trying to adapt to a new situation. For this situation, if the variable locationID=1 then I need all records, regardless of its locationID.

However, if it is not equal to 1, I need to only provide records that match that of the @locationID.

Here is my setup example:

DECLARE @locationID INT = 1; -- All Results Regardless of locationID
--DECLARE @locationID INT = 2; -- Only results that match this locationID (2)
--DECLARE @locationID INT = 3; -- Only results that match this locationID (3)

-- Temp Data
DECLARE @temp AS TABLE (color VARCHAR(10), locationID INT, name VARCHAR(20))
INSERT INTO @temp( color, locationID, name  )VALUES  ( 'Blue', 1, 'Test 1' )
INSERT INTO @temp( color, locationID, name ) VALUES  ( 'Red', 2, 'Test 2' )
INSERT INTO @temp( color, locationID, name ) VALUES  ( 'Red', 1, 'Test 3' )
INSERT INTO @temp( color, locationID, name ) VALUES  ( 'Red', 2, 'Test 4' )
INSERT INTO @temp( color, locationID, name ) VALUES  ( 'Red', 3, 'Test 5' )

-- Query
SELECT * 
FROM @temp
WHERE
locationID = ...

I am trying to figure out if I need to use a CASE WHEN or some other method for this.

5 Answers 5

7
WHERE (locationID = @LocationId OR @locationID = 1)
Sign up to request clarification or add additional context in comments.

9 Comments

Note that this solution is quite vulnerable to parameter sniffing. If the first time you run the query you pass a location of 1, you'll get a scan, because you need the whole table. Guess what happens when you pass a location of 2? Still get a scan even though it should probably be a seek this time.
@AaronBertrand that would be a bug in SQL server if that is the case. I'd have to look at the query plan to verify it is hitting the index. If it isn't, the query would need to be changed until the database is patched
Just Google parameter sniffing, this has been a common issue for literally decades, and it’s not a bug, it’s by design.
@AaronBertrand you are probably right (without looking at the plan), but I still think it's a bug, the database should be using "optimize for unknown" or something like that by default
SQL Server is using "optimize for not having to compile a new plan every time" - you may not like the behavior, but it's not a bug, it's most certainly by design. If you want to use optimize for unknown, you can add that hint to the query; if you really want the behavior all the time, you can use trace flag 4136 server-wide. SQL Server doesn't do this by default because this is a not a good choice for most queries. See this post for more information.
|
4

Try this:

...WHERE (@LocationId = 1 AND 1=1)
   OR (@LocationId <> 1 AND LocationId = @LocationId)

2 Comments

This matches the OP's requirements. You don't really need the 1=1 clause, though.
Yeah it’s just personal preference. Habit I guess.
1

This seems devilishly simple, perhaps I am not understanding your question. If you want to query by a variable value, then just use the variable value:

DECLARE @LocationId INT = 1

SELECT * 
FROM @temp
WHERE (@LocationId = 1 AND 1=1)
OR (@LocationId <> 1 AND LocationId = @LocationId)

4 Comments

If @locationID = 1, return ALL results, if its anything other than 1, it needs to only return results that match that ID
Wouldn't it be much simpler just to have where @locationId = 1 or @locationID=locationID?
Why the 1=1 clause?
Test to make sure the OR logic isn't interfering. You can toggle by 1=1 to 1=2.
1

A couple of other ideas. Since using a constant of 1 is not exactly intuitive or self-documenting, you could default that to NULL. And then say:

WHERE LocationID = COALESCE(@LocationID, LocationID);

If LocationID column is nullable, then instead you could use some token value that is at least slightly more self-documenting than 1, like -1 (since I don't think anyone looking at the code will immediately know that there isn't really a valid row where LocationID = 1):

WHERE LocationID = COALESCE(NULLIF(@LocationID, -1), LocationID);

This is susceptible to parameter sniffing, so you might want to add OPTION (RECOMPILE) if you find you are often switching between "all rows" and "very specific rows." You can also use dynamic SQL to optionally build the WHERE clause, assuming your real scenario uses a real table:

DECLARE @sql nvarchar(max) = N'SELECT ... FROM dbo.RealTable'
IF @LocationID <> 1 -- or IS NOT NULL or <> -1 or what have you
BEGIN
  SET @sql += N' WHERE LocationID = @LocationID';
END
EXEC sys.sp_executesql @sql, N'@LocationID int', @LocationID;

This way you get a different plan when the parameter is included vs. when it is not, which doesn't really make the scan (where you need all rows) any better, but it guards against the case where you use a seek + lookup against all rows, which can be really bad. More often, it prevents you from getting the dreadful full table/index scan when you really could have used a seek. But which way that goes is completely dependent on which case is more likely the first time the query runs.

And even here you may want to use OPTION (RECOMPILE) at the end of @sql if the distribution of data across different LocationID values is (or might later become) heavily skewed.

I call this type of query "the kitchen sink," and have written about it here:

Comments

1

So the variable should be 1 or the locationid? You could use an IN for that.

... WHERE @LocationID IN (1, LocationID)

Example snippet:

declare @locationID int;

-- Test data using a table variable
declare @varTable table (id int identity(1,1) primary key, locationID int, name varchar(20), color varchar(10));
insert into @varTable (locationID, name, color) values  
(1,'Test 1','Blue'),
(2,'Test 2','Red'),
(1,'Test 3','Red'),
(2,'Test 4','Red'),
(3,'Test 5','Red');

-- Returning all records
set @locationID = 1;
SELECT t.* FROM @varTable t WHERE @locationID IN (1, t.locationID);

-- Only returning those with locationID = 2
set @locationID = 2;
SELECT t.* FROM @varTable t WHERE @locationID IN (1, t.locationID);

1 Comment

Simple and clean, nice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.