For loop with two parameters in and multiple calculations out - plsql

Okay, there must be a better way... Given constants:
create or replace PACKAGE time_conversion_cons AS
c_day_to_hr CONSTANT NUMBER := 24;
c_day_to_min CONSTANT NUMBER := 1440;
c_day_to_sec CONSTANT NUMBER := 86400;
c_hr_to_day CONSTANT NUMBER := .0417;
c_hr_to_min CONSTANT NUMBER := 60;
c_hr_to_sec CONSTANT NUMBER := 3600;
c_min_to_day CONSTANT NUMBER := .000694;
c_min_to_hr CONSTANT NUMBER := .0167;
c_min_to_sec CONSTANT NUMBER := 60;
c_sec_to_day CONSTANT NUMBER := .0001157;
c_sec_to_hr CONSTANT NUMBER := .000478;
c_sec_to_min CONSTANT NUMBER := .167;
END time_conversion_cons; -- this package spec compiles and runs fine.
For the input parameters (n, units), where units are (days, hrs, min's, seconds),
convert to the other three and display results.
The following anonymous block compiles, runs and solves the problem. Is there a way to avoid typing all of the BEGIN...END four times, once for each pair of parameters?
DECLARE
v_n1 NUMBER := 2.5;
v_u1 VARCHAR2(10) := 'days';
v_n2 NUMBER := 1.8;
v_u2 VARCHAR2(10) := 'hours';
v_n3 NUMBER := 13;
v_u3 VARCHAR2(10) := 'minutes';
v_n4 NUMBER := 720;
v_u4 VARCHAR2(10) := 'seconds';
BEGIN
pri(v_n1||' '||v_u1||' is '||
time_conversion_cons.c_day_to_hr * v_n1||' hours or '||
time_conversion_cons.c_day_to_min * v_n1||' minutes or '||
time_conversion_cons.c_day_to_sec * v_n1||' seconds.');
END;

SQL>
SQL> create or replace PACKAGE time_conversion_cons AS
2 type dim1 is table of number index by varchar2(10);
3 type dim2 is table of dim1 index by varchar2(10);
4 l_matrix dim2;
5
6 l_tags sys.odcivarchar2list := sys.odcivarchar2list('day','hr','min','sec');
7 end;
8 /
Package created.
SQL>
SQL> create or replace PACKAGE body time_conversion_cons AS
2 begin
3 l_matrix('day')('hr'):= 24;
4 l_matrix('day')('min'):= 1440;
5 l_matrix('day')('sec'):= 86400;
6 l_matrix('hr')('day'):= .0417;
7 l_matrix('hr')('min'):= 60;
8 l_matrix('hr')('sec'):= 3600;
9 l_matrix('min')('day'):= .000694;
10 l_matrix('min')('hr'):= .0167;
11 l_matrix('min')('sec'):= 60;
12 l_matrix('sec')('day'):= .0001157;
13 l_matrix('sec')('hr'):= .000478;
14 l_matrix('sec')('min'):= .167;
15 END;
16 /
Package body created.
SQL>
SQL>
SQL> set serverout on
SQL> DECLARE
2 v_n1 NUMBER := 2.5;
3 v_u1 VARCHAR2(10) := 'day';
4 BEGIN
5 for i in 1 .. time_conversion_cons.l_tags.count loop
6 if time_conversion_cons.l_tags(i) != v_u1 then
7 dbms_output.put_line(v_u1||' to '||time_conversion_cons.l_tags(i));
8 dbms_output.put_line(v_n1*time_conversion_cons.l_matrix(v_u1)(time_conversion_cons.l_tags(i)));
9 end if;
10 end loop;
11 END;
12 /
day to hr
60
day to min
3600
day to sec
216000
PL/SQL procedure successfully completed.
SQL>
SQL>

Related

PLS-00201 Error in Pipelined Function in PL/SQL

I was trying to create a pipelined table function in PL/SQL but facing the below error. Is this an syntax error?
CREATE OR REPLACE FUNCTION FUNC_IDS(
IDS_IN IN VARCHAR2
) RETURN IDS_T
PIPELINED
IS
BEGIN
select * from dual;
return;
END func_ids;
Script Output:
Function FUNC_IDS compiled
LINE/COL ERROR
--------- -------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
3/10 PLS-00201: identifier 'IDS_T' must be declared
Errors: check compiler log
I missed to create the row and table types before creating the func. Have created them later as below and trying to create the function that gets input as a string of IDs and pipe out the individual IDs to another function.
CREATE TYPE TF_ROW AS OBJECT (ID NUMBER);
CREATE TYPE IDS_T IS TABLE OF TF_ROW;
create or replace function func_ids (ids_in in varchar2) return ids_t pipelined is
n_start pls_integer := 1;
n_end pls_integer := 1;
--
begin
loop
-- find the first and next comma in string
n_start := instr(ids_in, ',', n_start, 1);
n_end := instr(ids_in, ',', n_start, 2);
--
if (n_end <= 0) then
exit;
end if;
-- get the string and pipe it back out
pipe row (to_number(substr(ids_in, n_start+1, n_end - n_start - 1)));
-- ready for next one
n_start := n_end;
end loop;
return;
end func_ids;
Script Output:
Function FUNC_IDS compiled
LINE/COL ERROR
--------- -------------------------------------------------------------
22/5 PL/SQL: Statement ignored
22/15 PLS-00382: expression is of wrong type
Errors: check compiler log
What's wrong with the expression here?pipe row (to_number(substr(ids_in, n_start+1, n_end - n_start - 1)));
As you were told, IDS_T isn't declared.
Here's an example which works. See how I did it, do it yourself with your data.
Types:
SQL> create or replace type t_row is object (empno number, ename varchar2 (20));
2 /
Type created.
SQL> create or replace type t_tab is table of t_row;
2 /
Type created.
Function:
SQL> create or replace function func_ids (ids_in in varchar2)
2 return t_tab
3 pipelined
4 is
5 begin
6 for cur_r in (select empno, ename
7 from emp
8 where deptno = ids_in)
9 loop
10 pipe row (t_row (cur_r.empno, cur_r.ename));
11 end loop;
12
13 return;
14 end func_ids;
15 /
Function created.
Testing:
SQL> select * from table(func_ids (20));
EMPNO ENAME
---------- --------------------
7369 SMITH
7566 JONES
7788 SCOTT
7876 ADAMS
7902 FORD
SQL>
How to use the result returned by FUNC_IDS? I'm doing it using code you posted as a comment (the FUNC_ENAME function); I marked what you did wrong.
SQL> CREATE OR REPLACE FUNCTION func_ename (ids t_tab)
2 RETURN VARCHAR2
3 IS
4 l_tmp VARCHAR2 (100);
5 l_result VARCHAR2 (400);
6 BEGIN
7 l_result := ';';
8
9 FOR i IN ids.FIRST .. ids.LAST
10 LOOP
11 SELECT l.ename
12 INTO l_tmp
13 FROM emp l
14 WHERE l.empno = ids (i).empno; --> this line was wrong
15
16 l_result := l_result || l_tmp || ';';
17 END LOOP;
18
19 RETURN l_result;
20 END;
21 /
Function created.
SQL> SELECT func_ename (func_ids (10)) FROM DUAL;
FUNC_ENAME(FUNC_IDS(10))
------------------------------------------------------------------
;CLARK;KING;MILLER;
SQL>

How To solve this pl sql oracle query?

Write a PL/SQL program using While Loop to display all DEPTNO,DNAME and LOC
from DEPT table. Assuming the difference between two deptno is 10.
I am beginer and i am very confuse to solve this query please help to solve this problem.
Something like this?
SQL> set serveroutput on
SQL> declare
2 i dept.deptno%type; -- loop counter
3 l_max_deptno dept.deptno%type; -- upper limit
4 l_dept_row dept%rowtype; -- will contain the whole DEPT table row
5 begin
6 -- MIN -> i (which will be the starting point); MAX -> l_max_deptno (which will be the end)
7 select min(deptno), max(deptno)
8 into i, l_max_deptno
9 from dept;
10
11 while i <= l_max_deptno
12 loop
13 -- select the whole row into L_DEPT_ROW
14 select *
15 into l_dept_row
16 from dept
17 where deptno = i;
18
19 dbms_output.put_line(l_dept_row.deptno ||' - '||
20 l_dept_row.dname ||' - '||
21 l_dept_row.loc);
22 -- increment counter by 10 (because, as you said, the difference is 10)
23 i := i + 10;
24 end loop;
25 end;
26 /
10 - ACCOUNTING - NEW YORK
20 - RESEARCH - DALLAS
30 - SALES - CHICAGO
40 - OPERATIONS - BOSTON
PL/SQL procedure successfully completed.
SQL>
Here is how you do what you asked for, but bear in mind this is not the optimum way to do it. if your target is to train your self on the While loop in PLSQL, then here you go.
DECLARE
CURSOR C_DEPTS
IS
SELECT DEPTNO, DNAME, LOC FROM DEPT;
V_DEPTNO VARCHAR2 (255);
V_DNAME VARCHAR2 (255);
V_LOC VARCHAR2 (255);
BEGIN
OPEN C_DEPTS;
FETCH C_DEPTS INTO V_DEPTNO, V_DNAME, V_LOC;
WHILE C_DEPTS%FOUND
LOOP
DBMS_OUTPUT.PUT_LINE ('DEPTNO = ' || V_DEPTNO);
DBMS_OUTPUT.PUT_LINE ('DNAME = ' || V_DNAME);
DBMS_OUTPUT.PUT_LINE ('LOC = ' || V_LOC);
FETCH C_DEPTS INTO V_DEPTNO, V_DNAME, V_LOC;
END LOOP;
END;

ORA-24344: success with compilation error

It says procedure created with compilation errors.
`create or replace procedure fact(n in number, r out number);
i number:=n
f number:=1;
begin
while(i>1) loop
f:=f*1;
i=i-1;
r=f;
end fact;`
Quite a few syntax errors; compare your code with mine, find the differences:
SQL> set serveroutput on
SQL> CREATE OR REPLACE PROCEDURE fact (n IN NUMBER, r OUT NUMBER)
2 AS
3 I NUMBER := n;
4 f NUMBER := 1;
5 BEGIN
6 WHILE (i > 1)
7 LOOP
8 f := f * i;
9 i := i - 1;
10 END LOOP;
11
12 r := f;
13 END fact;
14 /
Procedure created.
SQL>
SQL> DECLARE
2 l_out NUMBER;
3 BEGIN
4 fact (4, l_out);
5 DBMS_OUTPUT.put_line (l_out);
6 END;
7 /
24
PL/SQL procedure successfully completed.
SQL>
Though, why did you choose to create a procedure? A function might be more appropriate in this case:
SQL> CREATE OR REPLACE FUNCTION f_fact (n IN NUMBER)
2 RETURN NUMBER
3 IS
4 i NUMBER := n;
5 f NUMBER := 1;
6 BEGIN
7 WHILE (i > 1)
8 LOOP
9 f := f * i;
10 i := i - 1;
11 END LOOP;
12
13 RETURN f;
14 END;
15 /
Function created.
SQL>
SQL> SELECT f_fact (4) FROM DUAL;
F_FACT(4)
----------
24
SQL>

Inaccurate result of an arithmetic operation when execute in procedure or function (oracle)

I'm trying to calculate item affected on employee salary but the result of arithmetic operation is inaccurate as example return 99.99999999999999 instead of 100 . This happen only inside procedure or function but work correctly in case in separated sql query .
EX :
CREATE TABLE SS_TEST_CUT
(
CUT NUMBER
);
create or replace procedure ss_test (x1 number , x2 number , num out number)
as
x number ;
begin
x := 100;
num := (x/30) *(30 - x1 - x2);
insert into ss_test_cut (cut) values (trunc (num,2));
end;
select cut from ss_test_cut;
Actual result is : 99.99
Expected result : 100
Out side the procedure gives the expected result .
select (100/30 ) * 30 from dual;
output : 100
why and how to avoid this with out round the number because other numbers have fraction part ?
Thanks,
Re-order the operations in the calculation to avoid intermediate rounding:
create or replace procedure ss_test (x1 number , x2 number , num out number)
as
x number ;
begin
x := 100;
num := (x * (30 - x1 - x2)) / 30;
insert into ss_test_cut (cut) values (trunc (num,2));
end;
Works as intended, assuming x1 and x2 are zero.
Best of luck.
Edit: OK, now that the question is slightly clearer, the procedure is producing 99.9 recurring, which is not the same thing as 100, especially when you round down. However I still don't see PL/SQL giving an incorrect result where SQL is correct. As Bob's answer shows, 100 / 30 * 30 and (100 * 30) / 30 are two different things.
This is a display formatting issue (Edit: unless you round down...):
SQL> select 100 / 30 * 30 from dual;
100/30*30
----------
100
SQL> select to_char(100 / 30 * 30) from dual;
TO_CHAR(100/30*30)
----------------------------------------
99.9999999999999999999999999999999999999
99.9 recurring and 100 are effectively the same thing.
Here is another example to show that the result inside the procedure is the same as when using SQL directly:
SQL> create table demo (label varchar2(10), result number);
Table created.
SQL> insert into demo values ('SQL', 100 / 30 * 30);
1 row created.
SQL> declare
2 num number;
3 begin
4 num := 100 / 30 * 30;
5
6 insert into demo values ('PL/SQL', num);
7 end;
8 /
PL/SQL procedure successfully completed.
SQL> select label, result, to_char(result) from demo;
LABEL RESULT TO_CHAR(RESULT)
---------- ---------- ----------------------------------------
SQL 100 99.9999999999999999999999999999999999999
PL/SQL 100 99.9999999999999999999999999999999999999
2 rows selected.

How to setup dbms_job with out parameter

I'm trying to run some procedures in parallel using dbms_jobs but i'm having some issues doing it. When trying to run below code, i'm getting this error
20:28:16 Info: Job #16 could not be executed. ORA-12011: execution of 1 jobs failed
ORA-06512: at "SYS.DBMS_IJOB", line 469
ORA-06512: at "SYS.DBMS_JOB", line 282
ORA-06512: at line 1
declare
ln_dummy number;
p_stdate CONSTANT DATE := '01-MAY-2012';
p_edate CONSTANT DATE := '31-MAY-2012';
p_cdate CONSTANT DATE := '09-FEB-2013';
p_key CONSTANT INTEGER:= 0;
p_ercode INTEGER;
p_erdesc VARCHAR2(200);
begin
COMMIT;
DBMS_JOB.SUBMIT(ln_dummy,'MY_PROC_1('''|| p_stdate ||''','''|| p_edate ||''','''|| p_cdate||''','''|| p_key ||''', :p_ercode, :p_erdesc: );');
COMMIT;
end;
/
p_ercode and p_erdesc is an out parameter in MY_PROC_1. If I try to comment it out the job runs without an issue.
My question is how can I run the job without commenting out p_ercode and p_erdesc in MY_PROC_1.
Also, is there a way to know which job is running and which job is already done? Something like an alert?
Maybe, this will work?
declare
ln_dummy number;
p_stdate CONSTANT DATE := '01-MAY-2012';
p_edate CONSTANT DATE := '31-MAY-2012';
p_cdate CONSTANT DATE := '09-FEB-2013';
p_key CONSTANT INTEGER:= 0;
begin
COMMIT;
DBMS_JOB.SUBMIT(ln_dummy,'
declare
p_ercode INTEGER;
p_erdesc VARCHAR2(200);
begin
MY_PROC_1('''|| p_stdate ||''','''|| p_edate ||''','''|| p_cdate||''','''|| p_key ||''', p_ercode, p_erdesc );
end;
');
COMMIT;
end;
if you want to log the outputs you can create a table:
SQL> create table log_table
2 (
3 job_id number,
4 start_time date,
5 end_time date,
6 retcode number,
7 retstr varchar2(4000)
8 )
9 /
Table created.
then execute the job. i've made a wrapper procedure just to make it a bit neater but you could put all of this in the job if you wanted.
SQL> create or replace procedure my_proc_1_job(
2 p_job number,
3 p_stdate date,
4 p_edate date,
5 p_cdate date,
6 p_key integer)
7 is
8 v_errcode integer;
9 v_errdesc varchar2(32767);
10 v_start_date date;
11 begin
12 v_start_date := sysdate;
13 my_proc_1(p_stdate, p_edate, p_cdate, p_key, v_errcode, v_errdesc);
14 insert into log_table (job_id, start_time, end_time, retcode, retstr)
15 values (p_job, v_start_date, sysdate, v_errcode, v_errdesc);
16 commit;
17 end;
18 /
Procedure created.
SQL> declare
2 ln_dummy number;
3 p_stdate CONSTANT varchar2(20) := '01-MAY-2012';
4 p_edate CONSTANT varchar2(20) := '31-MAY-2012';
5 p_cdate CONSTANT varchar2(20) := '09-FEB-2013';
6 p_key CONSTANT INTEGER:= 0;
7 begin
8 COMMIT;
9 DBMS_JOB.SUBMIT(
10 ln_dummy,
11 'my_proc_1_job(
12 JOB, to_date('''|| p_stdate ||''',''DD-MON-YYYY''),
13 to_date('''|| p_edate ||''',''DD-MON-YYYY''),
14 to_date('''|| p_cdate ||''',''DD-MON-YYYY''),
15 ' || p_key || ');');
16 COMMIT;
17 end;
18 /
PL/SQL procedure successfully completed.
SQL> select * from log_table;
JOB_ID START_TIM END_TIME RETCODE
---------- --------- --------- ----------
RETSTR
----------------------------------------------------------------------------------------------------
4 10-FEB-13 10-FEB-13 -1
failure
now all runs are recorded in LOG_TABLE.

Resources