How to I add a constant into a with block? - oracle

In a with block, I can define function and query, I would like to define a constant too that I can reuse after.
the following code works.
WITH
FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
b(v) AS (SELECT column_value FROM sys.ODCINUMBERLIST(1,2,3))
SELECT a (v) FROM b;
but this doesn't work.
WITH
c integer:=1;
FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
b(v) AS (SELECT column_value FROM sys.ODCINUMBERLIST(1,2,3))
SELECT a (v) FROM b;
[Error] Execution (14: 20): ORA-00942: table or view does not exist
I know that I can create another query in the with block:
select 1 c from dual
But it doesn't look pretty if I have a lot of constants.
Is there a way to add constant in a with block?

Add another subquery factoring clause with exactly one row and define all the constants in there and then CROSS JOIN it to your other queries:
WITH FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
constants (c1, c2, c3, c4) AS (
SELECT 1, 2, 3, 4 FROM DUAL
),
b(v) AS (
SELECT column_value FROM sys.ODCINUMBERLIST(1,2,3)
)
SELECT a(v), c.*
FROM b
CROSS JOIN constants c;
Or, you could declare a function for each constant:
WITH FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
FUNCTION c1 RETURN INTEGER IS BEGIN RETURN 1; END;
FUNCTION c2 RETURN INTEGER IS BEGIN RETURN 2; END;
FUNCTION c3 RETURN INTEGER IS BEGIN RETURN 3; END;
FUNCTION c4 RETURN INTEGER IS BEGIN RETURN 4; END;
b(v) AS (
SELECT column_value FROM sys.ODCINUMBERLIST(1,2,3)
)
SELECT a(v), c1, c2, c3, c4
FROM b;
(However, using functions may introduce a performance overhead with repeated context-switches between SQL and PL/SQL scopes. Personally, I would use the first option.)
Which both output:
A(V)
C1
C2
C3
C4
2
1
2
3
4
3
1
2
3
4
4
1
2
3
4
db<>fiddle here

Related

how to define multiple (WITH AS FUNCTIONS) in a single query?

trying to define multiple functions under a single with clause. which we normally do for CTE's. But for functions the same is not working. Please suggest solutions please.
With function dt ( b as number)
return number is
n number ;
begin
select 1 into n;
return n ;
end ;
dt2 ( c as number)
return number is
n1 number ;
begin
select 1 into n;
return n1;
end ;
select dt(1) , dt2(1) from dual
when using only dt i am able to get o/p but not with dt2.
It is working with the following example:
with
function x(p_NUM in number) return number
is
n number;
begin
SELECT 1 INTO N FROM DUAL;
--
return N;
--
end ;
--
function Y(p_NUM in number) return number
is
N1 NUMBER;
begin
SELECT 2 INTO N1 FROM DUAL;
--
return N1;
--
end ;
--
select X(1), Y(1)
from dual;
Cheers!!

Lowest fraction Value In Oracle

I am trying to create a function to return lowest fraction value. the sample code is here :
create or replace function fraction_sh(x number) return varchar2
is
fra1 number;
pwr number;
intprt number;
v4 number;
numer number;
denom number;
gcdval number;
frac varchar2(50);
begin
if x <> 0 then
fra1 := mod(x,1);
pwr := length(mod(x,1))-1;
intprt := trunc(x);
numer :=mod(x,1)*power(10,length(mod(x,1))-1);
denom :=power(10,length(mod(x,1))-1);
gcdval := gcdnew(power(10,length(mod(x,1))-1),mod(x,1)*power(10,length(mod(x,1))-1));
if intprt = 0 then
frac := to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
DBMS_OUTPUT.put_line(1||' '||denom||' '||gcdval||' '||numer);
else
frac := (intprt*to_char(trunc(denom/gcdval)))+to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
DBMS_OUTPUT.put_line(2||' '||denom||' '||gcdval||' '||numer);
end if;
end if;
return frac;
end;
create or replace function gcdnew (a number, b number, p_precision number default null, orig_larger_num number default null) return number is
v_orig_larger_num number := greatest(nvl(orig_larger_num,-1),a,b);
v_precision_level number := p_precision;
begin
if a is null or b is null or (a = 0 and b = 0) then return 1; end if;
if p_precision is null or p_precision <= 0 then
v_precision_level := 4;
end if;
if b is null or b = 0 or (b/v_orig_larger_num <= power(10,-1*v_precision_level) and greatest(a,b) <> v_orig_larger_num) then
return a;
else
return (gcdnew(b,mod(a,b),v_precision_level,v_orig_larger_num));
end if;
end;
Inmost cases it works, but when i try to pass 2/11 it returns 2/10.
Any help appreciated.
The problem with what you're currently doing is precision. With 2/11 the resulting number is 0.1818181... recurring, and the length of that - and therefore the pwr value - end up as 40, which destroys the later calculations.
With modifications to limit the precision (and tidied up a bit, largely to remove repeated calculations when you have handy variables already):
create or replace function fraction_sh(p_float number) return varchar2
is
l_precision pls_integer := 10;
l_int_part pls_integer;
l_frac_part number;
l_power pls_integer;
l_numer number;
l_denom number;
l_gcdval number;
l_result varchar2(99);
begin
if p_float is null or p_float = 0 then
return null;
end if;
l_int_part := trunc(p_float);
l_frac_part := round(mod(p_float, 1), l_precision);
l_power := length(l_frac_part);
l_denom := power(10, l_power);
l_numer := l_frac_part * l_denom;
l_gcdval := gcdnew(l_denom, l_numer, ceil(l_precision/2));
if l_int_part = 0 then
l_result := trunc(l_numer/l_gcdval) ||'/'|| trunc(l_denom/l_gcdval);
else
l_result := l_int_part * (trunc(l_denom/l_gcdval) + trunc(l_numer/l_gcdval))
||'/'|| trunc(l_denom/l_gcdval);
end if;
return l_result;
end;
/
Which gets:
with t(n) as (
select 9/12 from dual
union all select 2/11 from dual
union all select 1/2 from dual
union all select 1/3 from dual
union all select 1/4 from dual
union all select 1/5 from dual
union all select 1/6 from dual
union all select 1/7 from dual
union all select 1/8 from dual
union all select 1/9 from dual
union all select 1/10 from dual
union all select 4/3 from dual
union all select 0 from dual
union all select 1 from dual
)
select n, fraction_sh(n) as fraction
from t;
N FRACTION
---------- ------------------------------
.75 3/4
.181818182 2/11
.5 1/2
.333333333 1/3
.25 1/4
.2 1/5
.166666667 1/6
.142857143 1/7
.125 1/8
.111111111 1/9
.1 1/10
1.33333333 4/3
0
1 1/1
So you might want to add some handling for either passing in 1, or the approximation after rounding ending up as 1/1 - presumably just to return a plain '1' in either case.
I've set l_precision to 10 rather arbitrarily, you can make that larger, but will hit problems at some point so test carefully with whatever value you pick.
(And I haven't looked at gdcnew at all; that can probably be simplified a bit too.)
you can use like this:
create or replace function fraction_sh(dividing number,divided number) return varchar2
is
dividing2 number;
divided2 number;
frac varchar2(100 char);
temp number;
loop_value boolean;
begin
loop_value:=true;
dividing2:=dividing;
divided2 :=divided;
if dividing <> 0 then
while loop_value
loop
if gcd(dividing2,divided2)<> 1 then
temp:=gcd(dividing2,divided2);
dividing2:=dividing2/temp;
divided2 :=divided2/temp;
frac:=dividing2||'/'||divided2;
else
loop_value:=false;
frac:=dividing2||'/'||divided2;
end if;
end loop;
else
frac:='0';
end if;
return frac;
end;
gcd func:
create or replace function gcd(a number, b number)
return number is
begin
if b = 0 then
return a;
else
return gcd(b,mod(a,b));
end if;
end;

oracle show table like cartesian coordinate system

I have a table
create table test_table(
id number(10),
x number(10),
y number(10),
svalue number(10));
with filling a table as
declare
i integer;
begin
i := 0;
for x in 1 .. 10 loop
for y in 1 .. 10 loop
i := i + 1;
insert into test_table
(Id, x, y, svalue)
values
(i, x, y, x + y);
end loop;
end loop;
commit;
end;
how I can show table like
1 2 3 4 5 Ny
1 2 3 4 5 6
2 3 4 5 6 7
3 4 5 6 7 8
Nx
where x - rows, y - columns, svalue - value x,y
if we want to get pivot output for Cartesian coordinate system
run below script for create pivot function
http://paste.ubuntu.com/21378705/
pivot function is actually ref cursor that give dynamic column output after ruining above script run query as
select * from table(pivot('select x,y,svalue from test_table'))
in above query select statement use as following manner
select rowsection,columnsection,data from table
i hope this will help.
Hear i achieve cartesian coordinate system using looping test_table.
declare
HEAD VARCHAR2(100);
CURSOR c1 IS SELECT distinct x rec FROM test_table order by x;
CURSOR c2 IS SELECT distinct y rec FROM test_table order by y;
begin
-- for disply header in y
for y in c2 loop
HEAD := HEAD || lpad(y.rec,4);
end loop;
DBMS_OUTPUT.put_line( lpad('X',3) || HEAD );
--
-- disply value with repect to x and y
for x in c1 loop
declare
STR VARCHAR2(100);
CURSOR c2 IS SELECT svalue rec FROM test_table where x= x.rec order by y;
begin
for y in c2 loop
STR := str || lpad(y.rec,4);
end loop;
DBMS_OUTPUT.put_line(lpad(x.rec,3) || STR);
end;
end loop;
--
end;
i hope this will help.

How to make an oracle cursor continue after it fetches a failed row?

Executing the following PL/SQL block:
declare
cursor c1 is
select rownum r1,
5/
case when r = 5 then 0 else 1 end
dzero, r from
(
SELECT RowNum r FROM dual CONNECT BY level <= 10
)
;
BEGIN
dbms_output.enable(buffer_size => NULL);
begin
FOR c1_rec in c1
LOOP
dbms_output.put_line('val: ' || c1_rec.r1);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error: '||SQLCODE||' -ERROR- '||SQLERRM);
end;
END;
Output is:
val: 1
val: 2
val: 3
val: 4
Error: -1476 -ERROR- ORA-01476: divisor is equal to zero
Given that the query of c1 unchanged, how to make the cursor continue to fetch the 6-10th rows, so that the output continue to show the followings?
val: 6
val: 7
val: 8
val: 9
val: 10
I think there's no way of catching that exception. The right thing to do would be to use NULLIF to avoid the division by zero but you would have to change the query:
DECLARE
CURSOR c1 IS
SELECT rownum r1,
5/NULLIF(CASE WHEN r = 5 THEN 0 ELSE 1 END, 0) dzero,
r
FROM (SELECT RowNum r FROM dual CONNECT BY level <= 10) ;
BEGIN
dbms_output.enable(buffer_size => NULL);
FOR c1_rec in c1 LOOP
dbms_output.put_line('val: ' || c1_rec.r1);
END LOOP;
END;

Updating multiple columns with function call

Supposing I have table with columns
a, b, c, d, e
Is there a way/syntax to update columns d and e from out parameters of procedure/function that takes in parameters values of columns a, b and c. (procedure(a, b, c, out d, out e))
(other than using a cursor to iterate through all rows one by one)?
You could create a PL/SQL tables or user defined records, like so
Function to process records
FUNCTION Get_updated_recs(p_emp_id emp.emp_id%TYPE,
p_manager_id emp.manager_id%TYPE)
RETURN EMP_REC_TYPE
IS
p_emp_rec EMP_REC_TYPE;
BEGIN
IF p_emp_id = 100 THEN
p_emp_rec.salary := 50000;
p_emp_rec.bonus := 10000;
END IF;
RETURN p_emp_rec;
END get_updated_recs;
Main program unit
DECLARE
TYPE emp_rec_type IS RECORD (
salary employees.salary%TYPE,
bonus employees.bonus );
emp_rec EMP_REC_TYPE;
BEGIN
FOR emp IN (SELECT *
FROM employees) LOOP
emp_rec := Get_updated_recs(emp.emp_id, emp.manager_id);
UPDATE employees
SET salary = emp_rec.salary,
bonus = emp_rec.bonus
WHERE emp_id = emp.emp_id;
END LOOP;
END;
I haven't tested/compiled it, just wrote out of my memory but something like this should work
With help of an Oracle expert I arrived at following solution:
CREATE TABLE testtable
(
a number,
b number,
c number,
d number
);
CREATE TYPE testobj AS OBJECT
(
x number,
y number
);
CREATE OR REPLACE FUNCTION testfun(a number, b number)
RETURN testobj IS
begin
return new testobj(x=>a+b, y=> a*b);
end;
INSERT INTO testtable VALUES (1,2,null,null);
INSERT INTO testtable VALUES (5,6,null,null);
UPDATE
(SELECT tt.*,
testfun1(a,b) fun_ret FROM testtable tt) talias
SET talias.c=talias.fun_ret.x,
talias.d=talias.fun_ret.y
;
Do as follows:
(1) Create and fill an example table:
CREATE TABLE TEST1
(
a NUMBER(10), b number (10), s number (10)
);
INSERT INTO TEST1 (a, b, s) VALUES (2, 3, NULL);
INSERT INTO TEST1 (a, b, s) VALUES (3, 12, NULL);
INSERT INTO TEST1 (a, b, s) VALUES (-2, 8, NULL);
(2) Create a simple function for the using in an UPDATE command:
CREATE OR REPLACE FUNCTION GET_SUM (x IN NUMBER, y IN NUMBER)
RETURN NUMBER
IS
x1 NUMBER(10) := x; y1 NUMBER (10):= y; s NUMBER(10) := NULL;
BEGIN
IF x1 IS NOT NULL AND y1 IS NOT NULL THEN s := x1 + y1; END IF;
RETURN s;
END;
(3) Use this function in the following UPDATE command:
UPDATE TEST1 SET s = GET_SUM (a, b);
(4) Check the updates:
select * from TEST1;

Resources