Usage of CASE Statements in WHERE Clause - PL/SQL - oracle

Can someone tell me what is the use of last 'Y' in the " end = 'Y') "
( case
when a = 'STAGE PAYMENT' then
'Y'
when b not IN ('To be Received', 'Received') then
'N'
when c != (d - NVL(e, 0) - NVL(f, 0) - NVL(g, 0)) then
'Y'
when NVL(h, 0) + NVL(i, 0) + NVL(j, 0) <> 0 then
case
when c != k then
'Y'
when (-l != NVL(e, 0) + NVL(f, 0) + NVL(g, 0) + NVL(m, 0)) then
'Y'
else 'N'
end
else 'N'
end = 'Y')
Also, is there any way of optimizing this?
Thanks!

Last end = 'Y') is nothing but the comparision.
Your case statement is generating one value based on condition and if it is Y then that condition will be statisfied and row will be considered in the result.
Lets say, If a = 'STAGE PAYMENT' is true for some record then your case statement will generate Y as output, which will be again compared with last end = 'Y') and returns true.
Cheers!!

As your case statement is part of the where clause, that = 'Y' is there to form the predicate.
Predicates most often have the form of <some value> <comparison operator> <other value> (there are exceptions, most notably regexp_like), where the <some value> and <other value> can be columns, functions, literal values, expressions, variables, etc.
Your case expression is simply taking up the place of the <some value>, i.e.:
select ...
from ...
where <some_col> = 1
and case .... end = 'Y'

Related

How to optimize nested CASE statements in PL/SQL

I am quite new to PL/SQL.
I am using the following nested CASE statement in a SELECT query. This follows the WHERE clause.
However, the query takes about 6 minutes to complete because of this nesting. If at least one CASE block is removed the query only takes about 1 minute to complete.
Is there any way to optimize this query?
(case
when a = 'STAGE PAYMENT' then
'Y'
when b not IN ('To be Received', 'Received') then
'N'
when c != (d - NVL(e, 0) - NVL(f, 0) - NVL(g, 0)) then
'Y'
when NVL(h, 0) + NVL(i, 0) + NVL(j, 0) <> 0 then
case
when c != k then
'Y'
when (-l != NVL(e, 0) + NVL(f, 0) + NVL(g, 0) + NVL(m, 0)) then
'Y'
else 'N'
end
else 'N'
end = 'Y')
I have tried using IF-ELSE blocks, but it did not do any good.
What about aggregating the nested cases in the parent case with ANDs?
(case
when a = 'STAGE PAYMENT' then
'Y'
when b not IN ('To be Received', 'Received') then
'N'
when c != (d - NVL(e, 0) - NVL(f, 0) - NVL(g, 0)) then
'Y'
when NVL(h, 0) + NVL(i, 0) + NVL(j, 0) <> 0 and c != k then
'Y'
when NVL(h, 0) + NVL(i, 0) + NVL(j, 0) <> 0 and (-l != NVL(e, 0) + NVL(f, 0) + NVL(g, 0) + NVL(m, 0)) then
'Y'
-- this serves as else 'N' in your code
when NVL(h, 0) + NVL(i, 0) + NVL(j, 0) <> 0 then
'N'
else 'N'
end = 'Y')

For loop and its reverse based on if-condition/ternary operation in Oracle

In Oracle, I have the following almost identical SQL in an if-else block of a stored procedure:
if v_endsid = '15' then
FOR i IN 1..v_num LOOP --loop around the number from the beginning
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
END LOOP;
else
FOR i IN REVERSE 1..v_num LOOP --loop around the number from the last
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
END LOOP;
end if;
As you can see, the only difference between the SQL in the if and else blocks is the FOR loop. One is a forward for loop, another is a reversed (backward) for loop.
Is there any way to combine the block? I am trying this:
FOR i IN (case when v_endsid = '15' then 1..v_num else REVERSE 1..v_num end) LOOP
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
END LOOP;
But it gives me compilation error in the 1..v_num:
Found: '..' Expecting: END -or- ELSE -or- WHEN -or- OR -or- AND -or-
BETWEEN IN LIKE LIKE2 LIKE4 LIKEC MEMBER SUBMULTISET -or- ! != < <= <>
= > >= ^ ^= IS NOT ~
There is no way of dynamically changing the direction of the for loop. The only thing you can do here if you want to combine the two blocks is to use a basic loop
if v_endsid = '15' then
i := 1;
reverse := false;
else
i := v_num;
reverse := true;
end if;
LOOP --loop around the number from the beginning
v_item := TRIM(SUBSTR(v_str, (v_num - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
if reverse = true then
if i = 1 then
exit;
else
i := i - 1;
else
if i = v_num then
exit;
else
i := i + 1;
end if;
end if;
END LOOP;
The final solution I adapt is by using basic LOOP and some ternary operations:
i := case when v_endsid = '15' then 1 else v_num end; --evaluates from front or back depending on the case
Loop
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
--other queries
i := i + (case when v_endsid = '15' then 1 else -1 end);
exit when i = 0 or i = v_num + 1; --exceeds the elements
end loop;
This, I think, is a fairly neat working replacement for the original SQL
How about the another way round ? Instead of manipulating the loop condition encapsulate the code evaluating the break condition into a function that can be called from the different loops.
declare
v_reverse constant boolean := true;
-- your parameters and break rule can be arbitrary complex, mine is simple
-- as this is just a demonstration
function break(i in pls_integer) return boolean is
begin
return 13 = i;
end;
begin
if v_reverse
then
for i in reverse 1 .. 15
loop
dbms_output.put_line(i);
exit when break(i);
end loop;
else
for i in 1 .. 15
loop
dbms_output.put_line(i);
exit when break(i);
end loop;
end if;
end;
/

Multiple if statement in VB for ArcGIS

I have a shape-file with two columns (Thirri_2a and Thirri 2b) both are filled with positiv and negativ double values. No i want to summarize both in a new column named "Thirri". Therefore i used a if statement to check true values (not equal to 0).
Dim x As Double
If (([Thirri_2a] = 0) And ([Thirri_2b] = 0)) Then
x = 0
ElseIf (([Thirri_2a] = 0) And ([Thirri_2b] <> 0)) Then
x = [Thirri_2b]
ElseIf (([Thirri_2a] <> 0) And ([Thirri_2b] = 0)) Then
x = [Thirri_2a]
Else (([Thirri_2a] <> 0) And ([Thirri_2b] <> 0))
x = (( [Thirri_2a] + [Thirri_2b] )/2)
End If
ArcGIS gives me the wonderful Errormessage:
"Error 999999"
There should be no problem with that code other than the type specifier as #Ekkehard.Horner notes, but that entire block should go in the Pre-logic Script Code block.
In the following field, which should be labelled Thirri =, just put x.

Performance issues with LINQ query when including OR clause

The following LINQ to Entities query slows down substantially when the commented line below is included in the query. Is there a better way to phrase this?
The 'OR' clause should only take into account the following lines:
((o.Type_ID == (int) RecordEnums.RecordType.Lead) && (o.Item_ID == l1.Lead_ID))
|| ((o.Type_ID == (int)RecordEnums.RecordType.Opportunity)(o.Item_ID == o1.Opportunity_ID))
Full Query
return withItemsPending
? (from l1 in db.Leads
from o1 in db.Opportunities.Where(x => (x.Lead_ID == l1.Lead_ID) && (x.Company_ID == companyId)).DefaultIfEmpty()
from l2
in
db.Tasks.Where(
o =>
((o.IsCompleted ?? false) == false) &&
(o.TaskType_ID == typeId) &&
((o.Type_ID == (int) RecordEnums.RecordType.Lead) && (o.Item_ID == l1.Lead_ID))
//|| ((o.Type_ID == (int)RecordEnums.RecordType.Opportunity) && (o.Item_ID == o1.Opportunity_ID))
&&
(o.Due_Date > EntityFunctions.AddDays(DateTime.Now, -1)))
where (l1.Company_ID == companyId)
select l1)
: (from l1 in db.Leads where (0 == 1) select l1);
}
Here's the offending query:
SELECT Extent1.Lead_ID
FROM Leads AS Extent1 LEFT OUTER JOIN
Opportunities AS Extent2 ON Extent2.Lead_ID = Extent1.Lead_ID AND Extent2.Company_ID = 118 INNER JOIN
Tasks AS Extent3 ON 0 = (CASE WHEN ([Extent3].[IsCompleted] IS NULL) THEN CAST(0 AS bit) ELSE [Extent3].[IsCompleted] END) AND Extent3.TaskType_ID = 1 AND
5 = Extent3.Type_ID AND Extent3.Item_ID = Extent1.Lead_ID OR
4 = Extent3.Type_ID AND Extent3.Item_ID = Extent2.Opportunity_ID AND Extent3.Due_Date > DATEADD(day, - 1, SysDateTime())
WHERE (Extent1.Company_ID = 118)
If what you want is "give me the items that are not completed AND are due later than yesterday AND are EITHER Lead OR Opportunity", you need to add extra brackets around the 2 statements you want to OR. What you are currently saying is "give me the items that are either not completed AND Lead OR Opportunity AND due later than yesterday".
The code will then be like this:
return withItemsPending
? (from l1 in db.Leads
from o1 in db.Opportunities.Where(x => (x.Lead_ID == l1.Lead_ID) && (x.Company_ID == companyId)).DefaultIfEmpty()
from l2
in
db.Tasks.Where(
o =>
((o.IsCompleted ?? false) == false) &&
(o.TaskType_ID == typeId) &&
(((o.Type_ID == (int) RecordEnums.RecordType.Lead) && (o.Item_ID == l1.Lead_ID))
|| ((o.Type_ID == (int)RecordEnums.RecordType.Opportunity) && (o.Item_ID == o1.Opportunity_ID)))
&&
(o.Due_Date > EntityFunctions.AddDays(DateTime.Now, -1)))
where (l1.Company_ID == companyId)
select l1)
: (from l1 in db.Leads where (0 == 1) select l1);
}

How to work with Null in summation

I am doing tot := tot + v_row.SAVINGS; where tot is initialized to 0 and v_row.SAVINGS is initialized to null.
Now I using nvl function to get value 0 instead of null for v_row.Savings as I need 0 in summation if there is Null but when I return v_row, if summation value shows as 0 then I need to have null or blank space value in return, how can I do it.
Note: I am using this for reporting purpose and so if value of summation is zero then I should show null or blank space in report.
NULLIF(expr1, expr2) is logically equivalent to
CASE WHEN expr1 = expr 2 THEN NULL ELSE expr1 END
NVL(expr1, expr2)
tot may be NULL and v_row.SAVINGS may be NULL.
Use NVL to ensure a number is returned for the addition
Use NULLIF to return NULL if the sum is NULL
It may be more appropriate to coerce the tot to NULL just before you return.
Example:
tot := NULLIF(NVL(tot, 0) + NVL(v_row.SAVINGS, 0), 0)
This should work:
tot := case when tot + v_row.SAVINGS = 0 then null else tot + v_row.SAVINGS end;

Resources