Conditional Check in Where clause - sql

i have a procedure in which the below condition is to be written in a WHERE clause. How do I do that.
itemid is a parameter which can be null.
if itemid is available then add it to my where clause,else do nothing

Some people use this technique
... WHERE #itemid IS NULL OR tbl.itemid = #itemid
It guarantees though that you will never get an index seek on the itemid column.
A better approach if the table is at all big is to split the query up into 2 separate cases
IF(#itemid IS NULL)
SELECT foo FROM bar
ELSE
SELECT foo FROM bar WHERE itemid = #itemid
If the number of combinations is too large you can consider dynamic SQL. Be sure you understand SQL injection first.
Ref: Dynamic Search Conditions in T-SQL

e.g.
SELECT Something
FROM SomeTable
WHERE (#MyParam IS NULL OR SomeField = #MyParam)
AND AnotherField = 1
You'll want to test this in your specific scenario for performance. If it's a simple query i.e. without a lot of conditional parameters, you might want to try this instead for performance:
IF ( #MyParam IS NULL )
SELECT Something
FROM SomeTable
WHERE AnotherField = 1
ELSE
SELECT Something
FROM SomeTable
WHERE SomeField = #MyParam
AND AnotherField = 1

Related

Short circuit WHERE using CASE containing a CONTAINS

I'm attempting to write something like:
SELECT Id FROM SomeTable
WHERE
CASE WHEN (#param IS NULL) THEN
1
ELSE
CONTAINS([FullText],#param)
END = 1
but I can't seem to get SQL Server not to complain about the syntax. Is there a way to use CASE to short-circuit the CONTAINS search?
Even doing something like this doesn't seem to short-circuit:
CASE WHEN (#param IS NULL) THEN
1
ELSE
(CASE WHEN CONTAINS([FullText], #param ) THEN
1
ELSE
0
END)
END = 1
If you look at the execution plan you can see that case is translated to a series of if... else where both part are executed.
It seems like the only way to avoid execution of undesirable part is
if #param is null
select * from myTable
else
select * from myTable
where <expensive condition check>
Just simplify your query :
SELECT Id FROM SomeTable
WHERE #param IS NULL OR CONTAINS([FullText],#param)
So if #param is NULL it will not check for second condition (short circuit)
Since sql server doesn't guarantee short-circuit conditions, you can do something like this to avoid the null predicate error:
SELECT Id
FROM SomeTable
WHERE #param IS NULL
OR CONTAINS([FullText], ISNULL(#param, 'a'))
This way, if #param is null, you will not get an error. I'm not so sure about performance, however - if there is no short-circuit conditions it means that perhaps both parts of the where clause will evaluate and that might take a while.

SQL: How To Conditionally Adjust the WHERE Clause Depending on Passed Parameters and still work with UNION

I'm fairly inexperienced with SQL. I'm using SQL Server 2012.
Basically I want the WHERE clause of a SQL SELECT statement to be adjusted depending on the values of certain parameters passed to the stored procedure.
I'll also be UNIONing the result of this SELECT with another SELECT statement within the stored procedure.
The structure so far is as follows:
CREATE PROCEDURE dbo.procMyProcedure
#Param1 smallint = null
, #Param2 int = null
, ...
AS ...
--I have three separate SELECT statements, each catering for a specific scenario
SELECT ...
FROM ...
WHERE
(ER.EMR_Number = ISNULL(#Param1, ER.EMR_Number)) AND
(EE.EME_Number = ISNULL(#Param2, EE.EME_Number))
SELECT ...
FROM ...
WHERE
(EE.EME_Number = ISNULL(#Param2, EE.EME_Number))
SELECT ...
FROM ...
WHERE
(ER.EMR_Number = ISNULL(#Param1, ER.EMR_Number))
The statements are identical except for the WHERE clause. In my scenario, the first statement returns exactly 1 result. The second returns 119 results. The third returns 3 results.
I don't want to run all three statements. I want to merge them into one statement, with that single statement returning the same results as the three statements above, depending on the values of the passed (optional) parameters.
I can do this by wrapping the SELECTs in a nested IF ... ELSE IF structure, but then I can't use UNION. The following structure doesn't work:
IF ...
SELECT ...
ELSE IF ...
SELECT ...
ELSE IF ...
SELECT ...
UNION
SELECT ...
There must be a way to merge these into a single SELECT statement, in such a way that I can use it as part of a UNION statement.
I want to get to the following:
SELECT ... --This select
UNION
SELECT ... --Other stuff
I'd really appreciate any help with this.
Regards,
Adam.
OK. I've figured a solution to my problem. It involves some Boolean logic and some use of NULL and a fair number of brackets.
Here's the WHERE clause I was looking for:
WHERE
((ER.EMR_Number = ISNULL(#Param1, ER.EMR_Number)) AND
(EE.EME_Number = ISNULL(#Param2, EE.EME_Number)))
OR
((ER.EMR_Number = ISNULL(#Param1, NULL)) AND
(EE.EME_Number = ISNULL(#Param2, EE.EME_Number)))
OR
((ER.EMR_Number = ISNULL(#Param1, ER.EMR_Number)) AND
(EE.EME_Number = ISNULL(#Param2, NULL)))
It gives me the exact results I expected, AND I can still use the query in a UNION statement.
Of course, there may be a more elegant solution. If so, please post.
-Adam.
if the emr_number and eme_number are not nullable, just run the first query with an OR instead of an AND

How do you query an int column for any value?

How can you query a column for any value in that column? (ie. How do I build a dynamic where clause that can either filter the value, or not.)
I want to be able to query for either a specific value, or not. For instance, I might want the value to be 1, but I might want it to be any number.
Is there a way to use a wild card (like "*"), to match any value, so that it can be dynamically inserted where I want no filter?
For instance:
select int_col from table where int_col = 1 // Query for a specific value
select int_col from table where int_col = * // Query for any value
The reason why I do not want to use 2 separate SQL statements is because I am using this as a SQL Data Source, which can only have 1 select statement.
Sometimes I would query for actual value (like 1, 2...) so I can't not have a condition either.
I take it you want some dynamic behavior on your WHERE clause, without having to dynamically build your WHERE clause.
With a single parameter, you can use ISNULL (or COALESCE) like this:
SELECT * FROM Table WHERE ID = ISNULL(#id, ID)
which allows a NULL parameter to match all. Some prefer the longer but more explicit:
SELECT * FROM Table WHERE (#id IS NULL) OR (ID = #id)
A simple answer would be use: IS NOT NULL. But if you are asking for say 123* for numbers like 123456 or 1234 or 1237 then the you could convert it to a varchar and then test against using standard wild cards.
In your where clause: cast(myIntColumn as varchar(15)) like '123%'.
Assuming the value you're filtering on is a parameter in a stored procedure, or contained in a variable called #Value, you can do it like this:
select * from table where #Value is null or intCol = #Value
If #Value is null then the or part of the clause is ignored, so the query won't filter on intCol.
The equivalent of wildcards for numbers are the comparators.
So, if you wanted to find all positive integers:
select int_col from table where int_col > 0
any numbers between a hundred and a thousand:
select int_col from table where int_col BETWEEN 100 AND 1000
and so on.
I don't quite understand what you're asking. I think you should use two different queries for the different situations you have.
When you're not looking for a specific value:
SELECT * FROM table
When you are looking for a specific value:
SELECT * FROM table WHERE intcol = 1
You can use the parameter as a wildcard by assigning special meaning to NULL:
DECLARE #q INT = 1
SELECT * FROM table WHERE IntegerColumn = #q OR #q IS NULL
This way, when you pass in NULL; you get all rows.
If NULL is a valid value to query for, then you need to use two parameters.
If you really want the value of your column for all rows on the table you can simply use
select int_col
from table
If you want to know all the distinct values, but don't care how many times they're repeated you can use
select distinct int_col
from table
And if you want to know all the distinct values and how many times they each appear, use
select int_col, count(*)
from table
group by int_col
To have the values sorted properly you can add
order by int_col
to all the queries above.
Share and enjoy.

CASE in SELECT WHERE statement for stored procedure?

I need to filter a table based on ManufacturerID in a stored procedure BUT when a null ManufacturerID is passed in I need all orders.
Can I do this in the WHERE statement so I don;t have to have the entire query written twice in my SP?
WHERE
#param IS NULL OR field = #param
But note, this gets very inefficient if you scale it up to search multiple columns. (See http://www.sommarskog.se/dyn-search.html)
Example of inefficient scaled up version...
WHERE
(#param1 IS NULL OR field1 = #param1)
AND (#param2 IS NULL OR field2 = #param2)
AND (#param3 IS NULL OR field3 = #param3)
You can do this:
where t.ManufacturerID = #ManufacturerIDParam OR #ManufacturerIDParam is null
Try:
WHERE table.field = coalesce(#param, table.field)
This can also lead to parameter sniffing issues. If possible, might want to clean up the nulls first instead?

SQL: Where MYID = ANY?

In a SQL query, that contains
... WHERE MYID = #1 ....
I have to manage 2 cases
1) There is a filter on a column, #1 will be a number (1,2,X...)
2) There is no filter on that column, #1 will be ...? (ANY)
Is there something for this "any" (SQL Server 2005) ?
PS.
Obviously, I understand that I can remove the "where".
PPS.
I explain myself for better understanding: I have this query in the code, and would like to pass an integer as parameter when the filter is ON, and "something" when my filter is OFF.
if (filterOn)
GetFoos(fooID);
else
GetFoos("ANY");
GetFoos(param1): "select * from FOOS where FOOID = {0}", param1
Make a UNION ALL of the two statements:
SELECT *
FROM mytable
WHERE myid = #col
UNION ALL
SELECT *
FROM mytable
WHERE #col IS NULL
or just split them in an IF / ELSE block of a stored procedures.
Either way, the optimizer will be able to optimize the queries separately, completely ignoring one of them depending on the value of #col.
you could do something along this line:
where (myid = #id or #id is null)
so you will only filter when #id contains a value and not when it is null.
Just remove the where or and clause if there is no filter on that column.
If you don't want to filter on a particular column, just don't filter on that column. Remove the WHERE MYID = entirely
Having
"select * from FOO where FOOID = {0}", param
use param="FOOID" when there is no param to filter, this will give
select * from FOO where FOOID = FOOID // removing the filter