SQL case-when-else statement efficiency - sql

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

Related

How to add condition on where clause based on optional parameter using SQL Server

I have an 'optional' parameter in my stored procedure, if my parameter has certain value then I need to add a conditional where statement in my existing query.
I don't want to have a separate statement and basically here what I want to do it but I'm not sure if possible with SQL Server
#myRole varchar='' -- here's optional parameter
SELECT blah blah
FROM myTable
WHERE
<I_have_here_existing_conditions>
AND
here I want to add another condition based on my #myRole parameter-something like
if myRole='Employee' then add to where condition: myStatus = 'Y'*
here is one way :
select *
from yourtable
where
[other conditions]
and (#myRole <> 'Employee' OR myStatus = 'Y')
Use a CASE expression like this:
where <other conditions>
and case when #myRole = 'Employee' then myStatus else 'Y' end = 'Y'
Assuming #myrole is either null or a defined value,
select *
from Table
where ...
and (#myrole is null or (myStatus='Y' and #myrole='Employee'))
if it's always defined as '' then just use and (#myrole='' or

use Case in Where Clause SQL

Hi to make this short.
I need only the column when a condition is met.
if the condition is not met, then I can proceed running my Query without that column as validation.
CONDITION NEEDED.
REFERENCE TABLE - TABLE_A
inside TABLE_A there is PARAMETER_NAME AND PARAMETER_VALUE
REQUIRED: PARAMETER_NAME= 'SMS_SEND'
PARAMETER_VALUE = 'TRUE'
COLUMN FOR VALIDATION
INSIDE TABLE_B
INVALID_STAT ---> COLUMN
IF PARAMETER_NAME= 'SMS_SEND' and PARAMETER_VALUE = 'TRUE'
THEN I NEED TO CHECK INVALID_STAT IF ITS EQUAL TO 'Y'
ELSE
I DONT NEED TO VALIDATE INVALID_STAT AT ALL
WHERE (CASE
WHEN 1 = (SELECT DECODE (PARAMETER_VALUE, 'FALSE', 1, 2)
FROM TABLE_A
WHERE PARAMETER_NAME = 'SMS_SEND')
THEN
INVALID_STAT
ELSE
'Y'
END) = 'Y'
based upon your commentary, this will get you what you have asked for.
However, I doubt this is what you need. Please update your question as requested in the commentary above. If this does answer your question, please accept it as the answer and close the question.
I expect that the real requirement will be to wrap the following query in an outer join to a larger query (which you have not supplied) along with a coalesce to deal with the possible no rows scenario.
select
case
when PARAMETER_VALUE = 'FALSE' THEN 'Y'
else ''
end as SMS_Send_Ind
from Table_A
where PARAMETER_Name = 'SMS_SEND'
;
If there is no record in the table with Parameter_name = 'SMS_SEND', then the query will return no rows. Otherwise it will a Y or a blank as determined by the number of records in Table_A with parameter_name = 'SMS_SEND'

SQL use intermediate results

I have a column with numbers (float) that I would like to categorize and store a category as integer and as label (string). For now assume that the category is simply defined by the FLOOR(x).
This works:
SELECT salary,
FLOOR(salary) AS category_integer,
CASE WHEN FLOOR(salary) = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM test01
but I was wondering if I could use the intermediate variable 'category_integer' defined in the beginning of my query in a later part, something like this:
SELECT salary,
FLOOR(salary) AS category_integer,
CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM test01
but this is apparently not how SQL works. I've looked into Common table Expressions but got lost there. Is there a way to reuse intermediate variables in an SQL expression?
SQL Fiddle
I must have missed this but I couldn't find related questions so far.
You may resort to common table expressions - basically a query that produces a labelled result set you can refer to in subsequent queries.
Adapted to your example:
with cte as (
select salary
, floor(salary) as category_integer
from test01
)
SELECT salary
, category_integer
, CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM cte
;
Consult the reference for more details: CTE / WITH in pgSQL 9.6.
See it at work in SQL fiddle.
There are pre- and post-selection operations. For example order by and group by are post-selection instructions, distinct for example filters out duplicate results during the selection proces itself and as such duplicate results do not even enter the result set to be ordered or grouped.
When you use AS, you are telling PostgreSQL to take the result and put it in a column named category_integer in the output. You are not actually making a variable here that's available during query execution, as the result is only available after the query executes. As such, you can only do this with subselects where you have the result available as a virtual table in itself, where category_integer is a column in a table rather than a variable.
SELECT category_integer,
CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM (SELECT FLOOR(0) AS category_integer FROM test01) AS test02
https://www.postgresql.org/docs/current/static/queries-select-lists.html
https://www.postgresql.org/docs/current/static/queries-table-expressions.html#QUERIES-TABLE-ALIASES

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.

Single SQL statement to select row, but only if there is exactly one matching row

Consider the following SQL:
SELECT #Count = COUNT(*)
FROM Widgets
WHERE WidgetName = 'foo'
IF ( #Count = 1 ) BEGIN
SELECT #WidgetId = WidgetId
FROM Widgets
WHERE WidgetName = 'foo'
END
It effectively sets the #WidgetId value, but only if there is exactly one matching row that satisfies the query. The query above is trivial, but consider the case where the WHERE clause is rather expensive. Or if the WHERE clause is more complicated than a single, complete column. (WHERE WidgetName LIKE '%foo%') Is there a way to do a single query to assign the value to the parameter, but only when exactly one row matches, without repeating the WHERE clause?
This will work and won't require you to do more than one single SELECT statement. You will have a lot of freedom on changing the query thereafter, should you need to, without breaking anything.
SELECT #WidgetId = WidgetId
FROM Widgets
WHERE WidgetName = 'foo'
IF ##ROWCOUNT = 1
BEGIN
--Here you are certain that there's only one matching row found
END
ELSE
BEGIN
--Here zero or more than one records were found
END
If I'm understanding correctly, you can use a HAVING clause to successfully only set the #WidgetId variable when the COUNT is equal to one. Something like this should work:
SELECT #WidgetId = MAX(WidgetId)
FROM Widgets
WHERE WidgetName = 'Widget Name'
GROUP BY WidgetName
HAVING COUNT(*) = 1
In the above case, #WidgetId will be set to the MAX(WidgetId) if there is only a COUNT of one record after grouping by WidgetName.
I've created a fiddle that you can look at here: http://www.sqlfiddle.com/#!6/0e303/21