Oracle 11g Nested Case Statement Calculation - sql

In Oracle 11g, I am trying to get to a sell price from a query of data. Yes I can export this and write the code somewhere else, but I want to try to do this elegantly in the query.
I only seem to get the first part of the equation and not the last CASE where I use:
WHEN sales_code
What I am ultimately trying to do is take the result from the top and divide it by the bottom except in the case of SALE_CODE 4 where I add 1+1 or 2 to the top result and then divide by the equation.
round(to_number(price) *
CASE WHEN class_code='X'
THEN .48
ELSE .5
END * e1.set_qty +
CASE WHEN carton_pack_qty = '1'
THEN 0
ELSE (
CASE WHEN NVL(SUBSTR(size, 1,NVL(LENGTH(size) - 2,0)),1) > '35'
THEN 3.5
ELSE 3
END)
END +
CASE
WHEN sales_code='1' THEN 0 /(1-17/100)
WHEN sales_code='2' THEN 0 /(1-5/100)
WHEN sales_code='3' THEN 0 /(1-18/100)
WHEN sales_code='4' THEN 1+1 / (1-9.5/100)
WHEN sales_code='5' THEN 0 /(1-17/100)
WHEN sales_code='6' THEN 0 /(1-8/100)
WHEN sales_code='7' THEN 0 /((1-150)/100)
ELSE (100/100)
END,2) AS "Price",
I get a result from the query, but not the whole calculation. I tried this many other ways and there was always an error with parentheses or some other arbitrary error.
Any help would be appreciated.

I think this is your problem:
WHEN sales_code='1' THEN 0 /(1-17/100)
CASE returns a scalar, a number. You're trying to have it return the second half of the formula in your calculation. You need something more like this:
...
END +
CASE WHEN sales_code='4' THEN 1 ELSE 0 END /
CASE
WHEN sales_code='1' THEN (1-17/100)
WHEN sales_code='2' THEN (1-5/100)
WHEN sales_code='3' THEN (1-18/100)
WHEN sales_code='4' THEN (1-9.5/100)
WHEN sales_code='5' THEN (1-17/100)
WHEN sales_code='6' THEN (1-8/100)
WHEN sales_code='7' THEN ((1-150)/100)
ELSE 1 END ...
Actually, I'm not entirely sure what you're trying to do with sales_code='4', but that looks close.

I think I understand now what you are trying to do. Almost at least :-)
The first thing you should do is write down the complete formula with parentheses where needed. Something like:
final = ((price * class_code_factor * set_qty) + quantity_summand + two_if_sales_code4) * sales_code_factor
(That last part looks like a percentage factor, not a divisor to me. I may be wrong of course.)
Once you have the formula right, translate this to SQL:
ROUND
(
(
(
TO_NUMBER(price) *
CASE WHEN class_code = 'X' THEN 0.48 ELSE 0.5 END *
e1.set_qty
)
+
CASE WHEN carton_pack_qty = 1 THEN 0
ELSE CASE WHEN NVL(SUBSTR(size, 1,NVL(LENGTH(size) - 2,0)),1) > '35'
THEN 3.5
ELSE 3
END
END
+
CASE WHEN sales_code = 4 THEN 2 ELSE 0 END
)
*
CASE
WHEN sales_code = 1 THEN 1 - (17 / 100)
WHEN sales_code = 2 THEN 1 - (5 / 100)
WHEN sales_code = 3 THEN 1 - (18 / 100)
WHEN sales_code = 4 THEN 1 - (9.5 / 100)
WHEN sales_code = 5 THEN 1 - (17 / 100)
WHEN sales_code = 6 THEN 1 - (8 / 100)
WHEN sales_code = 7 THEN (1 - 150) / 100)
ELSE 1
END
, 2 ) AS "Price",
Adjust this to the formula you actually want. There are some things I want to point out:
Why is price not a number in your database, but a string that you must convert to a number with TO_NUMBER? That must not be. Store values in the appropriate format in your database.
In a good database you would not have to get a substring of size. It seems you are storing two different things in this column, which violates database normalization. Separate the two things and store them in separate columns.
The substring thing looks strange at that, too. You are taking the left part of the size leaving out the last two characters. It seems hence that you don't know the lenth of the part you are getting, so let's say that this can be one, two or three characers. (I don't know of course.) Now you compare this result with another string; a string that contains a numeric value. But as you are comparing strings, '4' is greater than '35', because '4' > '3'. And '200' is lesser than '35' because '2' < '3'. Is this really intended?
There are more things you treated as strings and I took the liberty to change this to numbers. It seems for instance that a quantity (carton_pack_qty) should be stored as a number. So do this and don't compare it to the string '1', but to the number 1. The sales code seems to be numeric, too. Well, again, I may be wrong.
In a good database there would be no magic numbers in the query. Knowledge belongs in the database, not in the query. If a class code 'X' means a factor of 0.48 and other class codes mean a factor of 0.5, then why is there no table of class codes showing what a class code represents and what factor to apply? Same for the mysterious summand 3 resp. 3.5; there should be a table holding these values and the size and quantity ranges they apply to. And at last there is the sales code which should also be stored in a table showing the summand (2 for code 4, 0 elsewise) and the factor.
The query part would then look something like this:
ROUND((price * cc.factor * el.set_qty) + qs.value + sc.value) * sc.factor, 2) AS "Price"

Breaking the dividend into a sub query worked and then adding parentheses around it to divide by in the main query worked.
(
select
style,
to_number(price) *
CASE WHEN class_code='X'
THEN .48
ELSE .5
END * set_qty +
CASE WHEN carton_pack_qty = '1'
THEN 1
ELSE (
CASE WHEN to_number(NVL(SUBSTR(size, 1,NVL(LENGTH(size) - 2,0)),1)) > 35
THEN 3.5
ELSE 3
END)
END as Price
FROM STYL1 s1,STY2 s2
WHERE s1.style=s2.style
) P1

Related

I have divided 2 column in sql lite and received a output. However, the decimals are too long and I want only 2 decimals. How do I get that?

Below is the query which I have used. I need the result only 2 decimals not more than that.
:sum(CAST(CASE When t.issue_resolved like 'TRUE' then 1 else 0 END AS Float) * 100 /
count(t.issue_resolved) AS finalscore
t.issue_resolved (1 column)
2022-03-19 80.97826086956522 - (I want this to be only decimals)
You can use round function
round(sum(CAST(CASE When t.issue_resolved like 'TRUE' then 1 else 0 END AS Float) * 100 / count(t.issue_resolved),2) AS finalScore

how would I use a for loop in my hive query

I am running a hive query in sparksql that goes like this:
select
GREATEST(
CASE WHEN x1 = 2 then y1+3 else -1,
case when x2 = 2 then y2+3 else -1,
case when xe = 2 then y2+3 else -1,
.....
case when x100 = 2 then y100+3 else -1
)
as greatest_sum
from table
I need to loop through about 100 columns for each row and get the greatest value after applying the case statements.
I have 100 case statements. I'm looking for a way to do this without repetition over 100 lines. how would I do this?
I have made the case statements simple in this example but I have each case spanning over 15 lines in my code which makes repetition not the best way to go.

After inserting to the table, digits after decimal point changes to 0

While SELECT'ing columns in Vertica, it shows normal numeric values:
SELECT nvl2(exposure_time_ms, ROUND(exposure_time_ms / 1000, CASE WHEN exposure_time_ms < 10000 THEN 1 ELSE 0 END), 0) :: numeric(12,1) AS exposure_seconds
1
But when I am inserting the same thing to the table, which has column 'exposure seconds' type NUMERIC(12,1), it changes all digits after the decimal point to 0:
2
Presumably, this is because the nvl2() calculation produces an integer. So, try this:
SELECT nvl2(exposure_time_ms,
ROUND(exposure_time_ms / 1000.0,
CASE WHEN exposure_time_ms < 10000 THEN 1 ELSE 0 END
), 0
)::numeric(12,1) AS exposure_seconds
I will say that it really doesn't make sense to round the values based on the magnitude. You should store the value "as is" and use rounding for presentation -- if it is really needed.

SQL that should never return anything, but does

I came across the following SQL statement:
SELECT A.NAME
FROM THE_TABLE A
WHERE A.NAME LIKE '%JOHN%DOE%'
AND ((A.NUM_FIELD/1) - (A.NUM_FIELD/2)*2 <> 0)
That last condition, "((A.NUM_FIELD/1) - (A.NUM_FIELD/2)*2 <> 0)" is what baffles me. Depening on the implementation of order of operations, it should always result to 0 or A.NUM_FIELD / 2.
How does SQL still return records from this view? If it always results to half the original value, why have it? (This is a delivered SQL package)
Probably integer division, so an odd NUM_FIELD is going to be one less.
MSDN says:
If an integer dividend is divided by an integer divisor, the result is
an integer that has any fractional part of the result truncated.
if the NUM_FIELD is an integer, and an odd one- then
(A.NUM_FIELD/1) - (A.NUM_FIELD/2)*2
is equal to one
What SQL implementation is this?
As noted,
(x/1) - (x/2)*2
is equivalent to
X - (2*(x/2))
which, if integer division is being performed yields 0 or 1 depending on whether the value is even or odd:
x x/2 2*(x/2) x-(2*(x/2))
- --- ------- -----------
0 0 0 0
1 0 0 1
2 1 2 0
3 1 2 1
4 2 4 0
...
if so, it seems like an odd way way of checking for odd/even values, especially since most SQL implementations that I'm aware of support a modulo operator or function, either x % y or mod(x,y).
The seemingly extraneous division by 1 makes me think the column in question might be floating point. Perhaps the person who coded the query is trying to check for jitter or fuzzyness in the low order bits of the floating point column?
if you modified the query to return all the intermediate values of the computation:
SELECT A.NAME as Name ,
A.NUM_FIELD as X ,
A.NUM_FIELD / 1 as X_over_1 ,
A.NUM_FIELD / 2 as X_over_2 ,
( X.NUM_FIELD / 2 )
* 2 as 2x_over_2 ,
( A.NUM_FIELD / 1 )
- ( A.NUM_FIELD / 2 ) * 2 as Delta ,
case when ( ( A.NUM_FIELD / 1 ) - ( A.NUM_FIELD / 2 ) * 2 ) <> 0
then 'return'
else 'discard'
end as Row_Status
FROM THE_TABLE A
WHERE A.NAME LIKE '%JOHN%DOE%'
and then executed it, what results do you get?

Interpreting coded field in SQL

Having this table, I would like to find the rows with Val fitting my Indata.
Tol field is a tolerance (varchar), that can be either an integer/float or a percentage value.
Row Val Tol Outdata
1 24 0 A
2 24 5 B
3 24 10 C
4 32 %10 D
5 32 1 E
Indata 30 for example should match rows 3 (24+10=34) and 4 (32-10%=28.8).
Can this be done in mySQL? CREATE FUNCTION?
This is going to be rather difficult to do in MySQL with that table and column design. How do you plan to differentiate what sort of comparison should be done? By doing a string comparison to see if your varchar field contains a percentage sign?
I would suggest breaking your tolerance field into (at least) two int/float columns, say tol and tol_pct. For flexibility, I would represent tol_pct as a decimal (10% => .10). Then, you can do a query that looks like:
select *
from table
where
(Indata between Val - tol and Val + tol)
or (Indata between Val * (1 + tol_pct) and Val * (1 - tol_pct))
I don't have a MySQL install to test it on, but this example is converted from Oracle sql syntax. You have to use string functions to determine if the tol is a percent and act accordingly to calculate the min and max range for that field. Then you can use a between clause.
select *
from (select t.*,
case when substr(tol, 1, 1) = '%' then
t.val * (1 + convert('.' + substr(tol, 2), number))
else
t.val + convert(tol, number)
end maxval,
case when substr(tol, 1, 1) = '%' then
t.val * (1 - convert('.' + substr(tol, 2), number))
else convert(t.val - tol, number)
end minval
from mytable
) t
where 30 between minval and maxval
;
Sure it can be done. But let me tell you that you better review your database design as it conflicts with normalization quite a bit. See http://en.wikipedia.org/wiki/Database_normalization for a quick overview.