Case clause execution procedure - sql

Hi i have a SQL which is extremely slow.
select case when (value=1)
then (select <Some Math Logic> from table where table.id=table_2.id)
else 'false' end
from table_2 where <where clause>
what i want to know is how does the case clause work..??
the part select <Some Math Logic> from table is working on huge table set.
in Java we see that for an if statement
if(condition_1==true)
{return output;}
else
{return false;}
if the if statement is false then the part inside it is never processed, then what i want to know is if it is the same in the oracle SQL also.
does it work on the following logic..??
Check Case condition
if true, process inside output
else process other output.
or does it take the below logic:
pre-process all sub-queries
Check Case condition
if true, display inside output
else display other output.
please help
edit: Guys i dont want to tune the query shown above... this is just a dummy one.
what i need to know is how the case clause works step by step.
please share if any one has some input for that

Does this solve your problem?
SELECT
COALESCE(<Some Math Logic>, 'false')
FROM table_2 T2
LEFT JOIN table T
ON T.Id = T2.Id
and T2.value = 1
WHERE <where clause>

It seems that the logic is exactly like that in java.
I used the following logic to test your scenario:
I created a dummy function as below which will just write something on the dbms_output and return only 10.
CREATE OR REPLACE
FUNCTION DISP_MSG
RETURN VARCHAR2
AS
ret_val VARCHAR2(20):='10';
BEGIN
dbms_output.enable;
dbms_output.put_line('executed');
RETURN ret_val;
END DISP_MSG;
then i created an anonymous block to test the same:
DECLARE
var VARCHAR2(100);
BEGIN
SELECT CASE WHEN (1!=1) THEN DISP_MSG ELSE '1' END INTO var FROM dual;
dbms_output.put_line('j = '||var);
END;
Output:
j = 1
but when i tried the below:
DECLARE
var VARCHAR2(100);
BEGIN
SELECT CASE WHEN (1=1) THEN DISP_MSG ELSE '1' END INTO var FROM dual;
dbms_output.put_line('j = '||var);
END;
Output:
executed
j = 10
so its quite clear what is the execution logic

Good question. You need to see the execution plan to know for sure. The database engine is free to use any algorithm it sees fit so long as it gets you the results you asked for.
It could even outer join table to get the results in anticipation of value = 1. Or it could run the select from table and store the results into a temporary table that it can scan when it runs the main query.
Most likely, however, it is running the subquery for every row where value = 1. Hard to tell without seeing the plan.
It also depends on the details of . Are you taking aggregates? If so, a true join may be impossible and it may have to recalculate the answer for every row. If it's looking at values right on the table rows, then it may be able to optimize that away.
If you take the case statement out, does the overall query perform much faster? Want to make sure you are analyzing the correct sub-query.

Related

Why doesn't this to_date work, when the results have been filtered to match my date format (Oracle SQL)

I have a table 'A' with one column (VARCHAR2). The table contains a row containing the text '01/01/2021' and another row with the text 'A'.
When I try to filter out 'A' and then to_date the remaining value, I get 'ORA-01858: a non-numeric character was found where a numeric was expected'. I've tried this in 2 ways.
select *
from tbl
where col <> 'A'
and to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
select *
from ( select *
from tbl
where col <> 'A')
where to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
I can understand why the first might not work, but in the second example, the to_date should ONLY ever see filtered data (i.e. '01/01/2020').
When I delete the value of 'A', the statement runs and I get my result back so it seems conclusive that the reason it isn't running is because it's trying to to_date the value of 'A', even though that should have been filtered out by then.
I have been able to replicate this using actual Oracle tables but unfortunately when I try and reproduce the tables using WITH AS, the query works and no error is encountered - another mystery!
Why doesn't this query work? The order of operation seems to be satisfied (and it works if I use WITH AS).
Oracle (and other databases) are under no obligation to evaluate the predicate applied to an inline view before evaluating the outer predicate. Frequently, in fact, from a performance optimization standpoint, you want the optimizer to push a selective predicate from an outer query into a view, inline view, or subquery. In this case, whether the query throws an error will depend on the query plan the optimizer chooses and which predicate it actually evaluates first.
As a quick hack, you can change the inline view to prevent predicates from being pushed. In this case, the presence of a rownum stops the optimizer from pushing the predicate. You could also use hints like no_push_pred to try to force the optimizer to use the plan you want
select *
from ( select t.*, rownum rn
from tbl t
where col <> 'A')
where to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
The issue with either of these quick hacks, though, is that some future version of the optimizer might have more options than you are aware of today so you may have problems in the future.
A better option is to rewrite the query such that you don't care what order the predicates are evaluated. In this case (depending on Oracle version), that's pretty easy since to_date allows you to specify a value when there is a conversion error
select *
from tbl
where col <> 'A'
and to_Date(col default null on conversion error,'DD/MM/YYYY') =
to_date('01/01/2020','DD/MM/YYYY');
If you're on an earlier version of Oracle or to_date is just an example of the actual problem, you can create a custom function that does the same thing.
create function safe_to_date( p_str in varchar2, p_fmt in varchar2 )
return date
is
begin
return to_date( p_str, p_fmt );
exception
when value_error
then
return null;
end safe_to_date;
select *
from tbl
where col <> 'A'
and safe_to_date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');

Oracle 11g, how to speed up an 'in' query

I was trying to create a view and needed to create a column that shows if the 'somenumber' column exists on other table or not. The code below worked but very slowly. Is it possible to declare a table as (select someNumber from veryYugeTable) and check on that one instead of sending this query for every single record or using some other way to speed up the view?
case
when someOtherTable.someNumber in(select someNumber from veryYugeTable) then 'exists'
else 'doesn't exist'
end as "someColumn"
The query looks fine. You should have an index on veryYugeTable.someNumber.
Sometimes the optimizer handles correlated subqueries better than non-correlated ones, so you can try:
case
when exists
(
select *
from veryYugeTable
where veryYugeTable.someNumber = someOtherTable.someNumber
) then 'exists'
else 'doesn''t exist'
end as "someColumn"
(Well, as this query does exactly the same as yours, the optimizer should get to the same execution plan, but this is not always the case.)
But as mentioned: Make sure first to have that index.
You might find you get some benefit with scalar subquery caching if you do something like:
coalesce((select 'exists'
from veryyugetable vyt
where vyt.somenumber = someOtherTable.someNumber
and rownum = 1),
'doesn''t exist') somecolumn
N.B. the and rownum = 1 is not necessary if vyt.somenumber is a unique column. Also, I nth the suggest to index the vyt.somenumber column.
use left join instead of putting the in clause in select:
left join veryYugeTable on someNumber = someOtherTable.someNumber
adjust your case when statement as follow:
case
when veryYugeTable.OtherColumn is null then 'doesn''t exist'
else 'exist'
end as "someColumn"

SQL Server Empty Result

I have a valid SQL select which returns an empty result, up and until a specific transaction has taken place in the environment.
Is there something available in SQL itself, that will allow me to return a 0 as opposed to an empty dataset? Similar to isNULL('', 0) functionality. Obviously I tried that and it didn't work.
PS. Sadly I don't have access to the database, or the environment, I have an agent installed that is executing these queries so I'm limited to solving this problem with just SQL.
FYI: Take any select and run it where the "condition" is not fulfilled (where LockCookie='777777777' for example.) If that condition is never met, the result is empty. But at some point the query will succeed based on a set of operations/tasks that happen. But I would like to return 0, up until that event has occurred.
You can store your result in a temp table and check ##rowcount.
select ID
into #T
from YourTable
where SomeColumn = #SomeValue
if ##rowcount = 0
select 0 as ID
else
select ID
from #T
drop table #T
If you want this as one query with no temp table you can wrap your query in an outer apply against a dummy table with only one row.
select isnull(T.ID, D.ID) as ID
from (values(0)) as D(ID)
outer apply
(
select ID
from YourTable
where SomeColumn = #SomeValue
) as T
alternet way is from code, you can check count of DataSet.
DsData.Tables[0].Rows.count > 0
make sure that your query matches your conditions

SQL Case statement returning true although should be false

I have a SQL statement that is something like this
SELECT t1.*
CASE
WHEN t1.COL1 = 0 THEN 0
WHEN
(t1.COL2 = 'val' OR t1.COL3 = 'val' etc) AND ((SELECT COUNT(*) FROM t1 WHERE etc...) > 0)
THEN Run same sub query
WHEN
something else
THEN defaultvalue
END as brokencase
Now, the second WHEN statement above, when run alone, is returning zero. However when it is run here, I am getting a bunch of null values back because that statement is somehow returning true.
So why is the count statement returning zero, but the WHEN statement thinks its true? I have left out a lot of details here for privacy reasons, but this is the general problem.
Usually when I see this, the programmer is missing an ELSE or DEFAULT case in their statements. To find out what scenario you're not capturing, set up a select like this:
SELECT DISTINCT columnTested1, columnTested2, columnTestedX, case statement
FROM blah
Show all the columns being tested, then your case, what you'll see in the result is the combination of columns that are causing your NULL. You need to capture those in your case statement or you'll continue to miss those options
When you test different columns in the different options, you might need multiple else statements to catch them all. Once you've identified the combination of columns causing you a problem, it becomes dead simple to fix the case statement.
If you want to exclude results that have null values you will have to make sure you are checking for that in your WHEN condition. You haven't specified your WHERE clause in your count query so I can't say for sure if this is the problem. It would look something like this:
SELECT COUNT(*) FROM t1 WHERE ExcludeValue IS NOT NULL

How to select an empty result set?

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
I'm using a stored procedure in MySQL, with a CASE statement.
In the ELSE clause of the CASE ( equivalent to default: ) I want to select and return an empty result set, thus avoiding to throw an SQL error by not handling the ELSE case, and instead return an empty result set as if a regular query would have returned no rows.
So far I've managed to do so using something like:
Select NULL From users Where False
But I have to name an existing table, like 'users' in this example.
It works, but I would prefer a way that doesn't break if eventually the table name used is renamed or dropped.
I've tried Select NULL Where False but it doesn't work.
Using Select NULL does not return an empty set, but one row with a column named NULL and with a NULL value.
There's a dummy-table in MySQL called 'dual', which you should be able to use.
select
1
from
dual
where
false
This will always give you an empty result.
This should work on most DBs, tested on Postgres and Netezza:
SELECT NULL LIMIT 0;
T-SQL (MSSQL):
SELECT Top 0 1;
How about
SELECT * FROM (SELECT 1) AS TBL WHERE 2=3
Checked in myphp, and it also works in sqlite and probably in any other db engine.
This will probably work across all databases.
SELECT * FROM (SELECT NULL AS col0) AS inner0 WHERE col0 IS NOT NULL;
SELECT TOP 0 * FROM [dbo].[TableName]
This is a reasonable approach to constant scan operator.
SELECT NULL WHERE FALSE;
it works in postgresql ,mysql, subquery in mysql.
How about this?
SELECT 'MyName' AS EmptyColumn
FROM dual
WHERE 'Me' = 'Funny'
SELECT * FROM (SELECT NULL) WHERE 0
In PostgreSQL a simple
SELECT;
works. You won't even get any columns labeled 'unknown'.
Note however, it still says 1 row retrieved.