Oracle CASE short-circuit not working in group by - oracle

I've found in the documentation of case statment that it uses short-circuit:
Oracle Database uses short-circuit evaluation. That is, 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.
Consequently, Oracle never evaluates a comparison_expr if a previous
comparison_expr is equal to expr. For a searched CASE expression, the
database evaluates each condition to determine whether it is true, and
never evaluates a condition if the previous condition was true.
But the following SQL is returning divisor is equal to zero:
WITH data AS (SELECT 1 AS cond, 10 AS num, 0 AS div FROM DUAL)
SELECT
CASE WHEN cond = 2 THEN (CASE WHEN MAX(div) = 0 THEN 0 ELSE SUM(num / div) END)
ELSE -1
END AS result
FROM data
GROUP BY cond
Any solution to avoid divisor is equal to zero error?
EDIT
This query works fine:
WITH data AS (SELECT 1 AS cond, 10 AS num, 0 AS div FROM DUAL)
SELECT
CASE WHEN cond = 2 THEN (CASE WHEN MAX(div) = 0 THEN 0 ELSE 1 END)
ELSE -1
END AS result
FROM data
GROUP BY cond

Use
CASE WHEN cond = 2
THEN SUM(case when div = 0 then 0 else num / div end)
ELSE -1
END

Related

Complex SQL condition in Spring Data Jpa Specification [duplicate]

Are the two statements below equivalent?
SELECT [...]
FROM [...]
WHERE some_col in (1,2,3,4,5) AND some_other_expr
and
SELECT [...]
FROM [...]
WHERE some_col in (1,2,3) or some_col in (4,5) AND some_other_expr
Is there some sort of truth table I could use to verify this?
And has precedence over Or, so, even if a <=> a1 Or a2
Where a And b
is not the same as
Where a1 Or a2 And b,
because that would be Executed as
Where a1 Or (a2 And b)
and what you want, to make them the same, is the following (using parentheses to override rules of precedence):
Where (a1 Or a2) And b
Here's an example to illustrate:
Declare #x tinyInt = 1
Declare #y tinyInt = 0
Declare #z tinyInt = 0
Select Case When #x=1 OR #y=1 And #z=1 Then 'T' Else 'F' End -- outputs T
Select Case When (#x=1 OR #y=1) And #z=1 Then 'T' Else 'F' End -- outputs F
For those who like to consult references (in alphabetic order):
Microsoft Transact-SQL operator precedence
Oracle MySQL 9 operator precedence
Oracle 10g condition precedence
PostgreSQL operator Precedence
SQL as understood by SQLite
I'll add 2 points:
"IN" is effectively serial ORs with parentheses around them
AND has precedence over OR in every language I know
So, the 2 expressions are simply not equal.
WHERE some_col in (1,2,3,4,5) AND some_other_expr
--to the optimiser is this
WHERE
(
some_col = 1 OR
some_col = 2 OR
some_col = 3 OR
some_col = 4 OR
some_col = 5
)
AND
some_other_expr
So, when you break the IN clause up, you split the serial ORs up, and changed precedence.
Arithmetic operators
Concatenation operator
Comparison conditions
IS [NOT] NULL, LIKE, [NOT] IN
[NOT] BETWEEN
Not equal to
NOT logical condition
AND logical condition
OR logical condition
You can use parentheses to override rules of precedence.
Query to show a 3-variable boolean expression truth table :
;WITH cteData AS
(SELECT 0 AS A, 0 AS B, 0 AS C
UNION ALL SELECT 0,0,1
UNION ALL SELECT 0,1,0
UNION ALL SELECT 0,1,1
UNION ALL SELECT 1,0,0
UNION ALL SELECT 1,0,1
UNION ALL SELECT 1,1,0
UNION ALL SELECT 1,1,1
)
SELECT cteData.*,
CASE WHEN
(A=1) OR (B=1) AND (C=1)
THEN 'True' ELSE 'False' END AS Result
FROM cteData
Results for (A=1) OR (B=1) AND (C=1) :
A B C Result
0 0 0 False
0 0 1 False
0 1 0 False
0 1 1 True
1 0 0 True
1 0 1 True
1 1 0 True
1 1 1 True
Results for (A=1) OR ( (B=1) AND (C=1) ) are the same.
Results for ( (A=1) OR (B=1) ) AND (C=1) :
A B C Result
0 0 0 False
0 0 1 False
0 1 0 False
0 1 1 True
1 0 0 False
1 0 1 True
1 1 0 False
1 1 1 True
Here's a variant of the '3-variable truth table' using booleans
WITH truth_values AS
(SELECT FALSE AS A,
FALSE AS B,
FALSE AS C
UNION ALL SELECT FALSE,
FALSE,
TRUE
UNION ALL SELECT FALSE,
TRUE,
FALSE
UNION ALL SELECT FALSE,
TRUE,
TRUE
UNION ALL SELECT TRUE,
FALSE,
FALSE
UNION ALL SELECT TRUE,
FALSE,
TRUE
UNION ALL SELECT TRUE,
TRUE,
FALSE
UNION ALL SELECT TRUE,
TRUE,
TRUE),
logics AS
(SELECT truth_values.*,
a
OR b
AND c AS no_parens, (a
OR b)
AND c AS or_parens
FROM truth_values)
SELECT *,
no_parens != or_parens AS parens_made_a_difference
FROM logics
ORDER BY a,
b,
c
With these results:
#
A
B
C
no_parens
or_parens
parens_made_a_difference
1
false
false
false
false
false
false
2
false
false
true
false
false
false
3
false
true
false
false
false
false
4
false
true
true
true
true
false
5
true
false
false
true
false
true
6
true
false
true
true
true
false
7
true
true
false
true
false
true
8
true
true
true
true
true
false
If 'parens_made_a_difference' is true, then the parentheses made a difference.

Adding parenthesis changes evaluation of condition

I'm starting to learn Scheme and stumbled on this curious thing:
Given the following procedure:
(define (test x y) (if (= x 0) 0 y ))
When I create a conditional, it evaluates "as expected" when I add parenthesis to it: (test 0 1) gives 0. But when I don't add parenthesis (and I use the same input) it evaluates to the false condition: test 0 1 gives 1.
Why is this?
If you write:
test 0 1
This is the same as:
(begin
test ; evaluate variable test => #<procedure:something...> or similar value
0 ; evaluate literal 0 => 0
1) ; evaluate literal 1 => 1
==> 1 ; because 1 is the last expression in the block it is the result of it.
When you do (test 0 1) you are calling the procedure you'll get by evaluating variable test with the two arguments 0 and 1 which gets evaluated to the numbers they represent. If you do substitution it becomes:
(if (= 0 0) ; if 0 is the same numeric value as 0
0 ; then evaluate 0
1) ; else evaluate 1
==> 0
The same in JavaScript:
const test = (x, y) => x === 0 ? 0 : y;
test;0;1
==> 1 // result in node is 1 since it was the last expression
test(0, 1); // calling the fucntion behind the variable test with the arguments 0 and 1
==> 0
So parentheses matters around stuff as they matter afdter stuff in JavaScript. Basically (one two three) in Scheme is one(two, three) in JS. Just adding parentheses around somtheing is to just add () after something in JS.

I'm not sure why this simple code does not work

I want someone to enter number "1", "2", or "3", and if the number is correct, want to say "ok". If not, I want to say "enter 1 or 2 or 3".
This is my code:
puts "enter 1 or 2 or 3"
num = gets.to_i
if num == 1 or 2 or 3
puts "ok"
else
puts "enter 1 or 2 or 3"
end
When I enter an incorrect answer such as "e" or "p", it still says "ok".
Why is it the case?
Let's first examine why you are obtaining incorrect results.
If a equals false or nil (is falsy, meaning logically false), a or b returns the value of b. If a equals any other value (is truthy, meaning logically true), a or b returns a.
Suppose we have an expression a op1 b op2 c, where op1 and op2 are operators (e.g., a == b or c). This could be evaluated (a op1 b) op2 c or a op1 (b op2 c), where parentheses have the highest precedence.
The precedence of Ruby's operators (most implemented as methods) is given here. Note that == has higher precedence than or. Moreover, for any given operator op, a op b op c is evaluated (a op b) op c.
The expression num == 1 or 2 or 3 is therefore evaluated
((num == 1) or 2) or 3
Now consider the value of this expression, depending on the value of num.
num = 1
((num == 1) or 2) or 3 => (true or 2) or 3 => true or 3 => true
num != 1
((num == 1) or 2) or 3 => (false or 2) or 3 => 2 or 3 => 2
Here are some ways to obtain your desired result.
(num == 1) or (num == 2) or (num == 3)
(num == 1) || (num == 2) || (num == 3)
[1, 2, 3].include?(num)
[1, 2, 3] & [num] == [num]
([num] - [1, 2, 3]).empty?
Because of the precedence rules for operators, the parentheses are not needed in the first two expressions, but it can be argued they clarify the code, at least for some readers. (I would include them.)
Regarding the choice between using or or ||, see this SO queston, particularly the second answer. In practice, or is rarely used.
See Array#include?, Array#& and Array#-.
To round out the possibilities, one could conceivably use a case statement.
case num
when 1, 2, 3 then true
else false
end
If, as here, the acceptable values of num form a range, one could write either of the following.
num.between?(1, 3)
(1..3).cover?(num)
(num >= 1) && (num <= 3)
See Comparable#between and Range#cover?. Again, the parentheses in the latter are optional.
In your code, num == 1 or 2 or 3 evaluates to true always, as 2 is considered logically true, and using an or`` operator with logically true value returns atrue` result always.
The correct way to compare is like this
puts "enter 1 or 2 or 3"
num = gets.to_i
if num == 1 or num == 2 or num == 3
puts "ok"
else
puts "enter 1 or 2 or 3"
end
Here, you are comparing the value of variable with right literal.

What's the fastest way to check if input String is a correct RPN expression?

I came across a task which makes you check if a String passed as an argument to your method/function is a correct statement in the Reverse Polish Notation sense. It can contain lowercase alphabet letters, operation signs and integers. Is there any faster way to check it than to read every character separately and actually try to evaluate the whole expression?
You don't have to evaluate the whole expression but you do need to split it into tokens, and you have to know the valence of every operator (that is, how many operands it takes). For simplicity, let the valence of an operand be 0; then do the following:
Set stack_size to 0;
For Each token In expression:
Set stack_size to stack_size + 1 - valence(token)
If stack_size <= 0: Report failure
If stack_size == 1: Report success
Else : Report failure
Examples using _ for unary minus.
expression: 3 4 + 1 * _
stack_size: 0 1 2 1 2 1 1 -> success
expression: 2 3 4 + 1 * _
stack_size: 0 1 2 3 2 3 2 2 -> failure (not 1 at the end)
expression: 2 3 + + 1 * _
stack_size: 0 1 2 1 0 -> failure (stack_size <= 0)
You can use a parse table to recognize reverse polish notation. It requires looking at each character, but it's fast.

Similar queries have way different execution times

I had the following query:
SELECT nvl(sum(adjust1),0)
FROM (
SELECT
ManyOperationsOnFieldX adjust1,
a, b, c, d, e
FROM (
SELECT
a, b, c, d, e,
SubStr(balance, INSTR(balance, '[&&2~', 1, 1)) X
FROM
table
WHERE
a >= To_Date('&&1','YYYYMMDD')
AND a < To_Date('&&1','YYYYMMDD')+1
)
)
WHERE
b LIKE ...
AND e IS NULL
AND adjust1>0
AND (b NOT IN ('...','...','...'))
OR (b = '... AND c <> NULL)
I tried to change it to this:
SELECT nvl(sum(adjust1),0)
FROM (
SELECT
ManyOperationsOnFieldX adjust1
FROM (
SELECT
SubStr(balance, INSTR(balance, '[&&2~', 1, 1)) X
FROM
table
WHERE
a >= To_Date('&&1','YYYYMMDD')
AND a < To_Date('&&1','YYYYMMDD')+1
AND b LIKE '..'
AND e IS NULL
AND (b NOT IN ('..','..','..'))
OR (b='..' AND c <> NULL)
)
)
WHERE
adjust1>0
Mi intention was to have all the filtering in the innermost query, and only give to the outer ones the field X which is the one I have to operate a lot. However, the firts (original) query takes a couple of seconds to execute, while the second one won't even finish. I waited for almost 20 minutes and still I wouldn't get the answer.
Is there an obvious reason for this to happen that I might be overlooking?
These are the plans for each of them:
SELECT STATEMENT optimizer=all_rows (cost = 973 Card = 1 bytes = 288)
SORT (aggregate)
PARTITION RANGE (single) (cost=973 Card = 3 bytes = 864)
TABLE ACCESS (full) OF "table" #3 TABLE Optimizer = analyzed(cost=973 Card = 3 bytes=564)
SELECT STATEMENT optimizer=all_rows (cost = 750.354 Card = 1 bytes = 288)
SORT (aggregate)
PARTITION RANGE (ALL) (cost=759.354 Cart = 64.339 bytes = 18.529.632)
TABLE ACCESS (full) OF "table" #3 TABLE Optimizer = analyzed(cost=750.354 Card = 64.339 bytes=18.529.632)
Your two queries are not identical.
the logical operator AND is evaluated before the operator OR:
SQL> WITH data AS
2 (SELECT rownum id
3 FROM dual
4 CONNECT BY level <= 10)
5 SELECT *
6 FROM data
7 WHERE id = 2
8 AND id = 3
9 OR id = 5;
ID
----------
5
So your first query means: Give me the big SUM over this partition when the data is this way.
Your second query means: give me the big SUM over (this partition when the data is this way) or (when the data is this other way [no partition elimination hence big full scan])
Be careful when mixing the logical operators AND and OR. My advice would be to use brackets so as to avoid any confusion.
It is all about your OR... Try this:
SELECT nvl(sum(adjust1),0)
FROM (
SELECT
ManyOperationsOnFieldX adjust1
FROM (
SELECT
SubStr(balance, INSTR(balance, '[&&2~', 1, 1)) X
FROM
table
WHERE
a >= To_Date('&&1','YYYYMMDD')
AND a < To_Date('&&1','YYYYMMDD')+1
AND (
b LIKE '..'
AND e IS NULL
AND (b NOT IN ('..','..','..'))
OR (b='..' AND c <> NULL)
)
)
)
WHERE
adjust1>0
Because you have the OR inline with the rest of your AND statements with no parenthesis, the 2nd version isn't limiting the data checked to just the rows that fall in the date filter. For more info, see the documentation of Condition Precedence

Resources