SQL Rounding to nearest between 2 numbers - sql

Example
Value entered by the user :34,26 (a+)
Values in the table:
34,14........A+
34,42........A++
34,52........A++
.
.
.
The value entered by the user is between a+ and a++. In order to find the value of user, take the average of the closest upper and closest lower value, rounding up to the upper value if the value entered by the user is greater than the average, if the value entered by the user is lower than the average. Can you help with the sql query that will round to the lower value?

There are probably many ways to do that; as far as I understood, you want to find value (stored in the table) which is the closest to value you enter as a "parameter". If that's so, Oracle-based example (you originally tagged the question as such).
With sample data:
SQL> set ver off
SQL> select * from test;
COL
----------
34,14
34,42
34,52
Query:
SQL> with
2 minimax as
3 -- find values in the TEST table that represent boundaries around entered value
4 (select (select max(t.col) from test t where &&val >= t.col) mincol,
5 (select min(t.col) from test t where &&val <= t.col) maxcol,
6 (select max(t.col) from test t) maxx,
7 (select min(t.col) from test t) minn,
8 v.val
9 from dual v
10 )
11 -- return the closest boundary
12 select
13 case when mincol is null then minn
14 when maxcol is null then maxx
15 when abs(&&val - mincol) < abs(&&val - maxcol) then mincol
16 else maxcol
17 end as result
18 from minimax;
Enter value for val: 34.26
RESULT
----------
34,14
Some more testing:
SQL> undefine val
SQL> /
Enter value for val: 31
RESULT
----------
34,14
SQL> undefine val
SQL> /
Enter value for val: 36
RESULT
----------
34,52
SQL> undefine val
SQL> /
Enter value for val: 34.42
RESULT
----------
34,42
SQL>

Related

How to add decimal places to numbers without them and add decimals to those with only one decimal column

So i have a column that is number by type, there are values in there with numbers like 8, 15.0, 89.1 or null. I need to add a decimal spot to them so they would look like 8.00, 15.00, 89.10 and the nulls replaced with 0.00.( there are hundreds of number values not just the 4 examples) I figured I needed to do an update with a case, the null part is simple enough
update table
set cost = case
when null then 0.00
not sure how to tackle the other three situations, any help is much appreciated !!
So i have a column that is number by type
To me, it looks like a presentation issue, not storage. Justin tried to convince you that you don't have to do anything about it, I suggest you re-read what he said.
Table looks like this:
SQL> create table test (col) as
2 select 8 from dual union all
3 select 15.0 from dual union all
4 select 89.1 from dual union all
5 select null from dual; --> only NULL should be replaced by 0
Table created.
SQL> update test set col = 0 where col is null;
1 row updated.
SQL>
If you don't do anything, this is the result:
SQL> select * from test;
COL
----------
8
15
89,1
0
SQL>
As of presentation: it depends on a tool you use. For example: if it were SQL*Plus, you could format the column as
SQL> column col format 90D00
SQL> select * from test;
COL
------
8,00
15,00
89,10
0,00
SQL>
Comma is a decimal character in my database; change it to a dot:
SQL> alter session set nls_numeric_characters = '.,';
Session altered.
SQL> select * from test;
COL
------
8.00
15.00
89.10
0.00
SQL>
Or, in any tool, use the TO_CHAR function with appropriate format mask:
SQL> select col, to_char(col, '90D00', 'nls_numeric_characters = ''.,''') result from test;
COL RESULT
---------- ------
8 8.00
15 15.00
89,1 89.10
0 0.00
SQL>
Or, if it is some GUI tool (be it Oracle Forms, Reports, Apex, ...), it contains the Format mask property you should use for the same purpose - presentation - and put 90D00 into it to format value displayed to end users.
Therefore, once again: there's nothing wrong with values stored in that column; it is just about the way users will see them.

Oracle Insert Value Into Middle of Existing Value

This is similar to my previous question. I have the following partial docket numbers:
docket_number
-------------
2012JV592
2016DR138
2018JV84
If the docket number is less than 10 digits, then I need to insert 0's after the second letter until the lenth is 10 digits. Updated docket numbers would look like this.
docket_number
-------------
2012JV0592
2016DR0138
2018JV0084
One option might be to
split docket_number into two parts: first part contains digits and letters, the second contains the trailing number
result is concatenation of the first part and the second part left padded with zeroes up to total length of 10 characters
SQL> with test (docket_number) as
2 (select '2012JV592' from dual union all
3 select '2016DR138' from dual union all
4 select '2018JV84' from dual
5 ),
6 temp as
7 (select docket_number,
8 regexp_substr(docket_number, '[[:digit:]]+[[:alpha:]]+') part1,
9 regexp_substr(docket_number, '[[:digit:]]+$') part2
10 from test
11 )
12 select case when length(docket_number) < 10 then
13 part1 || lpad(part2, 10 - length(part1), '0')
14 else docket_number
15 end result
16 from temp;
RESULT
--------------------------------------------------------------------------------
2012JV0592
2016DR0138
2018JV0084
SQL>
How to update rows in a table? By using such a SELECT in UPDATE, e.g.
SQL> select * from test;
DOCKET_NUM
----------
2012JV592
2016DR138
2018JV84
SQL> update test a set
2 a.docket_number =
3 (with temp as
4 (select b.docket_number,
5 regexp_substr(b.docket_number, '[[:digit:]]+[[:alpha:]]+') part1,
6 regexp_substr(b.docket_number, '[[:digit:]]+$') part2
7 from test b
8 )
9 select case when length(t.docket_number) < 10 then
10 t.part1 || lpad(t.part2, 10 - length(t.part1), '0')
11 else docket_number
12 end
13 from temp t
14 where t.docket_number = a.docket_number
15 );
3 rows updated.
SQL> select * from test;
DOCKET_NUM
----------
2012JV0592
2016DR0138
2018JV0084
SQL>
You can split the data into three parts as digit group1, letters group, and digit group2 by using regexp_substr() functions, and lpad() function in order to add zeroes just before the second digit group, and then concatenate them directly by using || operators, assuming that you have the same data model for the whole table,
UPDATE t
SET docket_number = regexp_substr(docket_number,'[[:digit:]]+')||
regexp_substr(docket_number,'[[:alpha:]]+')||
lpad('0',10-length(docket_number),'0')||
regexp_substr(docket_number,'[[:digit:]]+$')
Demo
Is it the case that your docket_number should always follow the format 4 digits (year?) followed by 2 letters followed by 4 digits. Then simple sub-string of the docket_number and subsequent re-concatenation is sufficient.
select docket_number
, substr(docket_number,1,6) || lpad(nvl(substr(docket_number,7),'0'),4,'0')
from test_dn
where length(docket_number) < 10
order by docket_number;
and for update:
update test_dn
set docket_number = substr(docket_number,1,6) || lpad(nvl(substr(docket_number,7),'0'),4,'0')
where length(docket_number) < 10;
If the format holds true then, depending on table size, this could be significantly faster as regular expressions are relative slow.

Oracle need to remove decimal point

Simple and quick question
If I have column have values in decimals
number
10.20
13.4000
15.987
i Want to remove the decimal from display the output without decimal
number
1020
134000
15987
I have tried
select replace(number, '.','') from table
I got the result but is there any other way to solve it.
Apparently, values you're dealing with are strings, aren't they? Otherwise, you wouldn't have all those trailing zeros.
Anyway, if it happens that these are numbers after all, a little bit of mathematics might produce the desired result. For example:
SQL> with test (num) as
2 (select 10.201 from dual union
3 select 13.4000 from dual union
4 select 15.987 from dual
5 )
6 select num,
7 num * power(10, to_number(length(to_char(num - trunc(num))) - 1)) result
8 from test;
NUM RESULT
---------- ----------
10,201 10201
13,4 134
15,987 15987
SQL>

SQL complex grouping "in column"

I have a table with 3 columns (sorted by the first two):
letter
number (sorted for each letter)
difference between current number and previous number of the same letter
I'd like to calculate (with vanlla SQL) a fourth new column RESULT to group these data when the third column (difference of number between contiguos record; i.e #2 --> 4 = 5-1) is greater than 30 marking all the records of this interval with letter-number of the first record (i.e A1 for #1,#2,#3).
Since the difference between contiguos numbers makes sense just for records with the same letter, for the first record of a new letter, the value of differnce is 31 (meaning that it's a new group; i.e. #6).
Here is what I'd like to get as result:
# Letter Number Difference RESULT (new column)
1 A 1 1 A1
2 A 5 4 A1
3 A 7 2 A1
4 A 40 33 A40 (*)
5 A 43 3 A40
6 B 1 31 B1 (*)
7 B 25 24 B1
8 B 27 2 B1
9 B 70 43 B70 (*)
10 B 75 5 B70
Now I can only find the "breaking values" (*) with this query where they get a value of 1:
select letter
,number
,cast(difference/30 as int) break
from table
where cast(difference/30 as int) = 1
Even though I'm able to find these breaking values I can't finish my task.
Can anyone help me finding a way to obtain the column RESULT?
Thanks in advance
FF
As I understand you need to construct the last result column. You can use concat to do that:
SELECT letter
,number
,concat(letter, cast(difference/30 as int)) result
FROM table
HAVING result = 'A1'
after some exercise and a little help from a friend of mine, I've found a possible solution to my sql prolblem.
The only requirment for the solution is that my first record must have a value of 31 in Difference field (since I need "breaks" when Difference > 30 than the previous record).
Here is the query to get the column RESULT I needed:
select alls.letter
,alls.number
,ints.letter||ints.number as result
from competition.lag alls
,(select letter
,number
,difference
,result
from (select letter
,number
,difference
,case when difference>30 then 1 else 2 end as result
from competition.lag
) temp
where result = 1
) ints
where ints.letter=alls.letter
and alls.number>=ints.number
and alls.number-30<=ints.number

SQL function - Returning value for number of records in table

I am putting a bit of SQL into an Oracle script, if I run the Vanilla SQL i get the correct output of one single returned value/record. However in my custom function I get the value I am looking for returned as many times as there are records. Here is an example of what I have.
create function EXAMPLE_FUNCTION (passedID in NUMBER)
return NUMBER
IS
returnValue NUMBER;
BEGIN
SELECT "TABLE1"."ID" INTO returnValue
FROM "TABLE1" WHERE "TABLE1"."ID" = passedID;
RETURN returnValue;
END;
So if TABLE1 has 20 records I will get the record with ID 1 returned 20 times,
I am not sure where its going wrong, but I'm sure its simple!
You are probably calling the function like this:
select EXAMPLE_FUNCTION (1)
from my_table
Call it without the from:
select EXAMPLE_FUNCTION (1)
EDIT:
As pointed by Shannon Oracle requires the from clause so I searched and found examples using the dual table:
select EXAMPLE_FUNCTION (1)
from dual
Just do something like: SELECT EXAMPLE_FUNCTION(1) FROM dual;
Would something like the below work in your function?
Select COUNT(*) into returnValue
from TABLE1
where Table1.ID = passedID;
What value are you using for passedID? If you want to get soemthing different out for each row you need to pass something different in.
Compare these two function calls. First a fixed value:
SQL> select example_function(1) from table1
2 /
EXAMPLE_FUNCTION(1)
-------------------
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
20 rows selected.
SQL>
Then using the table column to supply the parameter:
SQL> select example_function(id) from table1
2 /
EXAMPLE_FUNCTION(ID)
--------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
20 rows selected.
SQL>