CASE WHERE change operator - TSQL - sql

I was wondering if you could do a case statement in the WHERE clause which changes the operator.
What I am trying to do is filter out results based on a boolean value.
SELECT *
FROM table1
WHERE
CASE #Status
WHEN 1 THEN Name LIKE 'SOMETHING%'
WHEN 2 THEN Name NOT LIKE 'SOMETHING%'
END
I was wondering if this is possible?

You can do it without a SWITCH expression, like this:
SELECT *
FROM table1
WHERE
(#Status = 1 AND Name LIKE 'SOMETHING%')
OR (#Status = 2 AND Name NOT LIKE 'SOMETHING%')
Since #Status can be equal to only one thing at a time, only one component of the OR expression would determine the outcome of the WHERE condition.

Related

Get the result of the first condition in the query

I want to make a query that searches the table for names of cities and that the result will be names of cities that start with the letter b and then names of cities that contain the letter b.
select *
from tb_city
where name like 'b%' or name like '%b%'
How can I first get the cities that start with the letter b and then the cities that contain the letter b?
The result I get is mixed
you need to use union all for this.
select *
from tb_city
where name like 'b%'
union all
select *
from tb_city
where name like '%b%' and name not like 'b%'
For conditional logic where you want to inspect and sort by the results of individual constraints you can use UNION, but you will need to add a discriminator or sort indicator column to identify the difference between the result sets. Note that this will however result in duplicate rows unless you deliberately exclude the 'b%' from the '%b%' result sets:
SELECT *, SortOrder = 0
FROM tb_city
WHERE name LIKE 'b%'
UNION ALL
SELECT *, SortOrder = 1
FROM tb_city
WHERE name like '%b%' and name NOT LIKE 'b%'
ORDER BY SortOrder
NOTE: only the last SELECT in the set can have the ORDER BY clause, the order is evaluated after the result sets are combined
An alternate solution that works well form multiple criteria, is to use a case statement to determine the sort. Using CASE will make it easier to ensure that you only get 1 of each record, without having to write the criteria as a mutually exclusive set:
SELECT *
FROM tb_city
WHERE name LIKE 'b%' or name LIKE '%b%'
ORDER BY CASE
WHEN name LIKE 'b%' THEN 0
WHEN name LIKE '%b%' THEN 1
ELSE 2
END
NOTE: It is good practise to specify a default constraint in your case statements, especially when using this conditional expression for sorting. If your WHERE criteria becomes out of sync with the CASE it might result in additional rows in the results, if no ELSE is specified then the extra rows will have a value of NULL, in SQL Server that will mean they will be sorted before the 0s. (In other RDBMS this behaviour is configurable and the defaults may not be what you expect...)
Remove any ambiguity be being explicit with your sorting and make sure you cover all bases.
The problem with a resultset like this that uses CASE directly in the ORDER BY though can be that it is hard to visualise why the results are rendered in that order, it can be hard to confirm the sequence logic.
If you want to debug the sorting criteria or just need to know why the data is sorted in a specific way, you can moe the CASE out to an expression based column in the set, like the UNION solution, we add in the discriminator column:
SELECT *
, CASE
WHEN name LIKE 'b%' THEN 0
WHEN name LIKE '%b%' THEN 1
ELSE 2
END as SortOrder
FROM tb_city
WHERE name LIKE 'b%' or name LIKE '%b%'
ORDER BY SortOrder
There is no significant performance difference between this and the previous query other than the additional bytes over the wire, by referencing the alias of the SortOrder expression the expression will not be re-evaluated.

SQL case-when-else statement efficiency

When I use this statement;
UPDATE TABLE
SET FIELD = CASE
WHEN NAME = 'a' THEN (SELECT NO FROM TABLE_TWO WHERE NAME = 'a')
ELSE 'x' END
WHERE FIELD_TWO = 1
if TABLE.NAME != 'a' will the select SQL executed nevertheless?
Moreover, a little extra question, do you think it is proper to have such logic in SQL code for any given product? I think having any logic in SQL makes its coverage very difficult, and hard to maintain, what do you think?
edit: select statement only returns a single value, ignore the case where it can return multiple values, that case is not in the scope of this question.
The Oracle manual claims that it does short-circuit evaluation:
Oracle Database uses short-circuit evaluation. For a simple CASE expression, the database evaluates each comparison_expr value only before comparing it to expr, rather than evaluating all comparison_expr values before comparing any of them with expr
In your case comparison_expr is the WHEN NAME = 'a' part and if the manual is right, the database will not run the select if name has a different value.
I think it would be easier to read and maintain, when you split it into two UPDATE-statements like this:
UPDATE TABLE SET FIELD = (SELECT TOP 1 NO FROM TABLE_TWO WHERE NAME = 'a')
WHERE FIELD_TWO = 1
AND NAME='a'
UPDATE TABLE SET FIELD = 'x'
WHERE FIELD_TWO = 1
AND NAME != 'a'
It lets you add more cases easily and you can generalize the cases if there are more of them, like:
UPDATE TABLE SET FIELD = (SELECT TOP 1 NO FROM TABLE_TWO WHERE NAME = TABLE.FIELD)
WHERE FIELD_TWO = 1
AND NAME IN ('a','b','c')
If I were you, I would use a variable so that case doesn't compute a scalar value everytime. Something like following:
DECLARE #myVar VARCHAR(10);
SELECT TOP 1 #myVar = NO FROM TABLE_TWO WHERE NAME = 'a';
UPDATE TABLE
SET FIELD = CASE
WHEN NAME = 'a' THEN #myVar
ELSE 'x' END
WHERE FIELD_TWO = 1

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.

CASE WHEN after THEN or Select value from other table

I need to do this:
SELECT
COLUMN1,
COLUMN2,
CASE SOMETHING WHEN 0 THEN 'SOMETHING'
ELSE
CASE SOMETHING1 WHEN 'SOMETHING2' THEN (Here I need my value from other table)
ELSE
...
ELSE
...
END
END
AS SOMETHINGSPECIAL
...
...
...
Entire select is horribly complicated sorry.
In the place after THEN in () I need to take out specific value from other table.
I have tried almost everything there is from joins, to put there SELECT WHERE or CASE WHEN statement it always end up with some error.
Keyword missing etc.
Also maybe problem is inside () there is long concatenate:
''
I need to put that specific value from other table into that concatenate.
It either doesn't want to allow me to use other CASE WHEN after that THEN or I'm doing something wrong.
EDIT (sorry cant post entire query dont wanna have problems in work):
SELECT
A.SOMETHING
CASE WHEN A.LIST_ID IN ('something','something') THEN ' (MY VALUE FROM OTHER TABLE HERE) '
END
AS SOMETHINGSPECIAL
FROM SOMETABLE
...
(MY VALUE FROM OTHER TABLE HERE) I tried to put there Select statement condition, Case statement to take out that one value from other table but it just gives me error.
You can use a correlated subquery:
(CASE SOMETHING
WHEN 0 THEN 'SOMETHING'
ELSE (CASE SOMETHING1
WHEN 'SOMETHING2' THEN (select value from othertable ot where ot.col = x.col)
ELSE . . .
Do note that you don't need nested cases. You could write this as:
(CASE WHEN SOMETHING = 0 THEN 'SOMETHING'
WHEN SOMETHING1 = 'SOMETHING2' THEN (select value from othertable ot where ot.col = x.col)
ELSE . . .
END)
You could try joining the table that the other values should come from, that's if there is a link between those tables, so these is what you should do
SELECT
T1.COLUMN1,
T2.COLUMN1,
T.The-column-you-want-from-table2
iNNER JOIN TABLE2 T2 ON T2.cOUMN1 = T1.COLUMN1
So there is no need for a case statement, because from what I can gather from your question, you want a representation value of a value from another table
Here is another more specific example,
Select
Transaction.OrderId
Transaction.OrderDate
Order.StatusDescription
From Transaction
Inner Join Order on Order.OrderID = Transaction.OrderID
In the above example, the StatusDescription is what need from the "other" table, this can be anything from paid,out-of-stock or whatever status. This status in your first table is represented by a number like 1 for paid, 2 for out of stock etc.....
Hope this helps

SQL: select all rows if parameter is null, else only select matching rows

I have a variable coming into a stored procedure. This variable can either have a value or be null.
If the variable is null, I need to select all the rows in the table (some with NULL values, some with actual data).
If the variable is not null, I only need to select the rows where the variable matches a column.
I created this conditional statement to help explain what I would like to achieve:
if
#status is null, select all the rows (rows with NULL for table.status, and rows with actual data for table.status)
else
select the rows where #status equals table.status
This is what I came up with (well one of them):
WHERE
book.book_nme LIKE #search_input AND
book.book_desc LIKE #search_input AND
(book.author LIKE ISNULL(#author, book.author)) AND
(bookStatus.status_desc LIKE ISNULL(#status, bookStatus.status_desc))
The only problem is that if bookStatus.status_desc is NULL, then it will not select that row (when #status is null)
I'm so confused, I tried looking up Coalesce too which seemed to prioritize the values, but ... I don't know what to do.
Should I just create a huge CASE in the stored procedure and have two select statements?
WHERE book.book_nme LIKE #search_input AND
book.book_desc LIKE #search_input AND
(#author IS NULL OR book.author LIKE #author) AND
(#status IS NULL OR bookStatus.status_desc LIKE #status) ...
Update
Added both conditions, for #author and #status
If you think it about your description it breaks down as:
Return all rows when #status is null
Otherwise return rows that match #status.
You can express the last line of your sql to match this like this:
(#status is null OR bookStatus.status_desc LIKE #status)
I always use this:
WHERE fieldname = ISNULL(#parameter, fieldname)
Hope this helps
Roger
where(field1 like isnull(#parameter,field1))
If the paramater is null will retrieve all data from field1
like
select field from table1
This works on sybase.