Oracle rounding issue - oracle

I am using this code to round down the decimal value to the next multiple of 25.
ie if value is 33.60 it should round to 33.50
create or replace
PROCEDURE "TEST1" (PQUERY IN VARCHAR2) as
prNspValue number(14,2) :='';
p_percentage_Value number(4,2) :='';
begin
prNspValue:=33.60;
dbms_output.put_line(prNspValue);
p_percentage_Value:=substr(prNspValue,instr(prNspValue,'.')+1,length(prNspValue));
dbms_output.put_line(p_percentage_value);
p_percentage_Value:=p_percentage_Value-mod(p_percentage_Value,25);
dbms_output.put_line(p_percentage_value);
if(p_percentage_Value!=0)then
prNspValue:=substr(prNspValue,1,instr(prNspValue,'.'))+p_percentage_Value/100;
else
prNspValue:=substr(prNspValue,1,instr(prNspValue,'.'));
end if;
dbms_output.put_line(prNspValue);
end;
but the problem is when the value is 33.60 it is taken as 33.6. so it is rounding it to 33.0.
How can i correct this code?

you can use the round function directly FLOOR(your_number*4)/4:
SQL> WITH my_data AS (
2 SELECT 33.00 num FROM dual
3 UNION ALL SELECT 33.10 FROM dual
4 UNION ALL SELECT 33.20 FROM dual
5 UNION ALL SELECT 33.30 FROM dual
6 UNION ALL SELECT 33.40 FROM dual
7 UNION ALL SELECT 33.50 FROM dual
8 UNION ALL SELECT 33.60 FROM dual
9 UNION ALL SELECT 33.70 FROM dual
10 UNION ALL SELECT 33.80 FROM dual
11 UNION ALL SELECT 33.90 FROM dual
12 ) SELECT num,
13 floor (num * 4) / 4 rounded
14 FROM my_data;
NUM ROUNDED
---------- ----------
33 33
33,1 33
33,2 33
33,3 33,25
33,4 33,25
33,5 33,5
33,6 33,5
33,7 33,5
33,8 33,75
33,9 33,75
10 rows selected

Related

PL SQL format number

Using PL SQL, I need to format number to VARCHAR2. I need to show decimal places if they are not zero. And decimal separator should be comma.
INPUT
OUTPUT
0.2
'0,2'
100.4
'100,4'
22
'22'
Would something like this do?
SQL> with test (input) as
2 (select 0.2 from dual union all
3 select 100.4 from dual union all
4 select 22 from dual
5 )
6 select input,
7 case when input = trunc(input) then to_char(input, '999G990')
8 else to_char(input, '999G990D0')
9 end output
10 from test;
INPUT OUTPUT
---------- ----------
,2 0,2
100,4 100,4
22 22
SQL>
this is locale independent, does not limit or round numbers
with
test(input) as (
select 0.2 from dual union all
select 100.4 from dual union all
select -12.34567 from dual union all
select 1.23e34 from dual union all
select -1.23e-34 from dual union all
select 22 from dual
)
select input
,case when input < 0 then '-' end -- sign
||case when abs(input) < 1 then '0' end -- zero
||replace(to_char(abs(input)),to_char(0,'fmd'),',') -- comma
output
from test
/

PL/SQL program using CURSOR for frequency distribution in table

finaltableA has two columns WORDS, WCOUNTS. The program should insert every word of intitaltableA into finaltableA exactly once. In the column WCOUNTS the program should put how often the word occurs.
intitaltableA
COVID
is
a
disease
COVID
can
be
treated
with
antibodies
COVID
is
a
serious
disease
there
is
a
vaccination
available
for
COVID
finaltableA should look like this -
WORDS
WCOUNTS
a
3
antibodies
1
available
1
be
1
can
1
COVID
4
disease
2
for
1
is
3
serious
1
there
1
treated
1
vaccination
1
with
1
I should use a cursor to insert and update the table. I am new to PL/SQL.
It is quite obvious that you can do this without any cursor in PLSQL, so I guess that means that you are trying to solve a homework problem or a training question. Anyway, here you can see two simple options to get the result you want, with and without PLSQL.
Option 1 - Without PLSQL
with x ( words )
as
(
select 'COVID' from dual union all
select 'is' from dual union all
select 'a' from dual union all
select 'disease' from dual union all
select 'COVID' from dual union all
select 'can' from dual union all
select 'be' from dual union all
select 'treated' from dual union all
select 'with' from dual union all
select 'antibodies' from dual union all
select 'COVID' from dual union all
select 'is' from dual union all
select 'a' from dual union all
select 'serious' from dual union all
select 'disease' from dual union all
select 'there' from dual union all
select 'is' from dual union all
select 'a' from dual union all
select 'vaccination' from dual union all
select 'available' from dual union all
select 'for' from dual union all
select 'COVID' from dual
)
select words, count(words) as counter
from x
group by words
order by 2 desc;
If you execute it, it gives you the output you want
SQL> with x ( words )
2 as
3 (
4 select 'COVID' from dual union all
5 select 'is' from dual union all
6 select 'a' from dual union all
7 select 'disease' from dual union all
8 select 'COVID' from dual union all
9 select 'can' from dual union all
10 select 'be' from dual union all
11 select 'treated' from dual union all
12 select 'with' from dual union all
13 select 'antibodies' from dual union all
14 select 'COVID' from dual union all
15 select 'is' from dual union all
16 select 'a' from dual union all
17 select 'serious' from dual union all
18 select 'disease' from dual union all
19 select 'there' from dual union all
20 select 'is' from dual union all
21 select 'a' from dual union all
22 select 'vaccination' from dual union all
23 select 'available' from dual union all
24 select 'for' from dual union all
25 select 'COVID' from dual
26 )
27 select words, count(words) as counter
28 from x
29 group by words
30* order by 2 desc
SQL> /
WORDS COUNTER
----------- ----------
COVID 4
is 3
a 3
disease 2
there 1
available 1
for 1
serious 1
antibodies 1
can 1
vaccination 1
WORDS COUNTER
----------- ----------
treated 1
be 1
with 1
14 rows selected.
Option 2 - Using PLSQL and a CURSOR
In the below example, I'm going to use dbms_output to show the results. Keep in mind that here there are dozens of ways to do the same, I am just using the simplest one I can think of.
SQL> create table t ( words varchar2(40) ) ;
Table created.
SQL> insert into t
2 with x ( words )
3 as
4 (
5 select 'COVID' from dual union all
6 select 'is' from dual union all
7 select 'a' from dual union all
8 select 'disease' from dual union all
9 select 'COVID' from dual union all
10 select 'can' from dual union all
11 select 'be' from dual union all
12 select 'treated' from dual union all
13 select 'with' from dual union all
14 select 'antibodies' from dual union all
15 select 'COVID' from dual union all
16 select 'is' from dual union all
17 select 'a' from dual union all
18 select 'serious' from dual union all
19 select 'disease' from dual union all
20 select 'there' from dual union all
21 select 'is' from dual union all
22 select 'a' from dual union all
23 select 'vaccination' from dual union all
24 select 'available' from dual union all
25 select 'for' from dual union all
26 select 'COVID' from dual
27* ) select words from x
SQL> /
22 rows created.
SQL> commit ;
Commit complete.
SQL> declare
2 begin
3 for i in ( select words, count(words) as counter from t group by words order by 2 desc )
4 loop
5 dbms_output.put_line( 'Words: '||i.words||' - Wcount is: '||i.counter||' ');
6 end loop;
7* end;
SQL> /
Words: COVID - Wcount is: 4
Words: is - Wcount is: 3
Words: a - Wcount is: 3
Words: disease - Wcount is: 2
Words: antibodies - Wcount is: 1
Words: for - Wcount is: 1
Words: be - Wcount is: 1
Words: vaccination - Wcount is: 1
Words: can - Wcount is: 1
Words: available - Wcount is: 1
Words: treated - Wcount is: 1
Words: there - Wcount is: 1
Words: with - Wcount is: 1
Words: serious - Wcount is: 1
PL/SQL procedure successfully completed.

Replace string in oracle

I have a number column, I need to replace the first number by 7 in oracle.
How to replace guys?
number want_number
4789654 7789654
2754678 7754678
1765689 7765689
For instance
REGEXP_REPLACE(number, '^\d', '7')
should work.
Or, a substring option with concatenation:
SQL> with test (num) as
2 (select 4789654 from dual union all
3 select 2754678 from dual union all
4 select 1765689 from dual
5 )
6 select num, '7' || substr(num, 2) wanted_num
7 from test;
NUM WANTED_NUM
---------- --------------------
4789654 7789654
2754678 7754678
1765689 7765689
SQL>
You could manipulate as numbers, rather than converting to (and presumably later back from) strings:
with your_table (original) as (
select 4789654 from dual
union all select 2754678 from dual
union all select 1765689 from dual
union all select 999 from dual
union all select 1000 from dual
union all select 1001 from dual
)
select original,
original
- trunc(original, -floor(log(10, original)))
+ 7 * power(10, floor(log(10, original))) as wanted
from your_table;
ORIGINAL WANTED
---------- ----------
4789654 7789654
2754678 7754678
1765689 7765689
999 799
1000 7000
1001 7001
The floor(log(10, original) gives you the magnitude of the number. As an example, for your first original value 4789654 that evaluates to 6. If you then do trunc(original, -floor(log(10, original))) that is trunc(4789654, -6), which zeros the six least significant digits, giving you 4000000. Subtracting that from the original value gives you 789654. Then power(10, floor(log(10, original))) gives you power(10, 6) which is 1000000, multiplying that by 7 gives you 7000000, and adding that back on gives you 7789654.
(This won't work if your original value is <= zero, but that looks unlikely?)

Oracle - creating a group based on group spacing

How to make a query that will create groups that have a space between them greater than "n"?
Data:
01-01-2000
02-01-2000
03-01-2000
06-01-2000
07-01-2000
19-02-2001
10-01-2002
11-01-2002
I would like to get a result for the interval between records, e.g. 2 days:
DATE GROUP
01-01-2000 1
02-01-2000 1
03-01-2000 1
06-01-2000 2
07-01-2000 2
19-02-2001 3
10-01-2002 4
11-01-2002 4
For 10 days:
01-01-2000 1
02-01-2000 1
03-01-2000 1
06-01-2000 1
07-01-2000 1
19-02-2001 2
10-01-2002 3
11-01-2002 3
Another example with integers:
with x as (
select 1 as A from dual
union all
select 2 as A from dual
union all
select 3 as A from dual
union all
select 10 as A from dual
union all
select 20 as A from dual
union all
select 22 as A from dual
union all
select 33 as A from dual
union all
select 40 as A from dual
union all
select 50 as A from dual
union all
select 100 as A from dual
union all
select 101 as A from dual
union all
select 102 as A from dual
) select A
from x;
I need to create groups for a value increase of more than 3:
Example result:
1 1
2 1
3 1
10 2
20 3
22 3
33 4
40 5
50 6
100 7
101 7
102 7
Here is one way to do it
CREATE TABLE TEST (
DATE_IN DATE
);
INSERT INTO TEST VALUES (TO_DATE('01-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('02-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('03-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('06-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('07-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('19-02-2001','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('10-01-2002','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('11-01-2002','DD-MM-YYYY'));
--HERE IS AN EXAMPLE FOR 1 DAY. Just change the value in the > 1 TO >10
--if you want to create a group if there is a gap of more than 10days
SELECT DATE_IN, SUM(NEW_GROUP) OVER ( ORDER BY DATE_IN) AS GROUPE FROM (
SELECT
DATE_IN,
CASE WHEN DATE_IN - LAG(DATE_IN,1,TO_DATE('01-01-1900','DD-MM-YYYY')) OVER ( ORDER BY DATE_IN) > 1 THEN 1 ELSE 0 END AS NEW_GROUP
FROM TEST
)
-- Result
DATE_IN GROUPE
2000-01-01T00:00:00Z 1
2000-01-02T00:00:00Z 1
2000-01-03T00:00:00Z 1
2000-01-06T00:00:00Z 2
2000-01-07T00:00:00Z 2
2001-02-19T00:00:00Z 3
2002-01-10T00:00:00Z 4
2002-01-11T00:00:00Z 4
Example with integer:
with x as (
select 1 as A from dual
union all
select 2 as A from dual
union all
select 3 as A from dual
union all
select 10 as A from dual
union all
select 20 as A from dual
union all
select 22 as A from dual
union all
select 33 as A from dual
union all
select 40 as A from dual
union all
select 50 as A from dual
union all
select 100 as A from dual
union all
select 101 as A from dual
union all
select 102 as A from dual
) SELECT A, SUM(NEW_GROUP) OVER ( ORDER BY A) AS GROUPE FROM (
SELECT
A,
CASE WHEN A - LAG(A,1,1) OVER ( ORDER BY A) > 5 THEN 1 ELSE 0 END AS NEW_GROUP
FROM X
)
order by A;

oracle select two count values in one row

I need help with this select in oracle. I have table with 2 columns: (table > date, value) for example:
1.1.2017, 16
1.1.2017, 16
1.1.2017, 16
1.1.2017, 17
1.2.2017, 16
1.2.2017, 16
1.2.2017, 17
1.2.2017, 17
1.3.2017, 16
Result must be:
1.1.2017 as date, 3 as count of 16, 1 as count of 17
1.2.2017, 2, 2
1.3.2017, 1, 0
Current SQL:
select date, count(value) from table group by date, value
However, this does not return the same date with one row with count of both values.
You need conditional counting, something like this:
with
your_table ( dt, value ) as (
select to_date('1.1.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.1.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.1.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.1.2017', 'mm.dd.yyyy'), 17 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 16 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 17 from dual union all
select to_date('1.2.2017', 'mm.dd.yyyy'), 17 from dual union all
select to_date('1.3.2017', 'mm.dd.yyyy'), 16 from dual
)
-- end of test data; solution (SQL query) begins below this line
select dt, count(case value when 16 then 'x' end) as ct_16,
count(case value when 17 then 'x' end) as ct_17
from your_table
group by dt;
DT CT_16 CT_17
---------- ----- -----
01/01/2017 3 1
01/02/2017 2 2
01/03/2017 1 0
select date, value, count(value) from table group by date, value;

Resources