PLS-00201 Error in Pipelined Function in PL/SQL - plsql

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>

Related

how to create index by using dynamic sql?

----------- it is giving below error ,where i did mistake ---------------
declare l1 integer;
begin execute immediate q '!
begin
execute immediate ' CREATE INDEX S_ACCNT_ATT_P1 ON S_ACCNT_ATT(ROW_ID) PARALLEL 4 ';
execute immediate ' CREATE INDEX S_ACCNT_ATT_U1 ON S_ACCNT_ATT(PAR_ROW_ID, FILE_NAME, FILE_EXT, CONFLICT_ID) PARALLEL 4 ';
execute immediate '
select 1 from dual '
into :l1;
end;!' using out l1;
end;
------- error -------------------
ORA-06550: line 2, column 27:
PLS-00103: Encountered the symbol "!
begin
execute immediate " when expecting one of the following:
The issue is the incorrect q-quoting syntax. That is causing the error you're seeing.
DECLARE
BEGIN
EXECUTE IMMEDIATE q '!BEGIN NULL; END;!';
END;
/
Error report -
ORA-06550: line 3, column 23:
PLS-00103: Encountered the symbol "!BEGIN NULL; END;!" when expecting one of the following:
. ( * # % & = - + ; < / > at in is mod remainder not rem
return returning <an exponent (**)> <> or != or ~= >= <= <>
and or like like2 like4 likec between into using || multiset
bulk member submultiset
The symbol "*" was substituted for "!BEGIN NULL; END;!" to continue.
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
There should be no space between the q and the ':
DECLARE
BEGIN
EXECUTE IMMEDIATE q'!BEGIN NULL; END;!';
END;
/
PL/SQL procedure successfully completed.
How/where exactly did you run that piece of code? I tested it on TOAD and SQL*Plus in Oracle 11g.
Sample table:
SQL> CREATE TABLE s_accnt_att
2 (
3 row_id NUMBER,
4 par_row_id NUMBER,
5 file_name NUMBER,
6 file_ext NUMBER,
7 conflict_id NUMBER
8 );
Table created.
Procedure:
SQL> SET SERVEROUTPUT ON;
SQL>
SQL> DECLARE
2 l1 INTEGER;
3 BEGIN
4 EXECUTE IMMEDIATE q'[
5 begin
6 execute immediate ' CREATE INDEX S_ACCNT_ATT_P1 ON S_ACCNT_ATT ( ROW_ID ) PARALLEL 4 ';
7 execute immediate ' CREATE INDEX S_ACCNT_ATT_U1 ON S_ACCNT_ATT ( PAR_ROW_ID , FILE_NAME , FILE_EXT , CONFLICT_ID ) PARALLEL 4 ';
8 execute immediate ' select 1 from dual '
9 into :l1;
10 end;
11 ]'
12 USING OUT l1;
13
14 DBMS_OUTPUT.put_line ('L1 = ' || l1);
15 END;
16 /
L1 = 1
PL/SQL procedure successfully completed.
SQL>
Seems to be OK.
As your target database is 19c, I tried it there as well; no problem.
SQL> select banner from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
SQL> DECLARE
2 l1 INTEGER;
3 BEGIN
4 EXECUTE IMMEDIATE q'[
5 begin
6 execute immediate ' CREATE INDEX S_ACCNT_ATT_P1 ON S_ACCNT_ATT ( ROW_ID ) PARALLEL 4 ';
7 execute immediate ' CREATE INDEX S_ACCNT_ATT_U1 ON S_ACCNT_ATT ( PAR_ROW_ID , FILE_NAME , FILE_EXT , CONFLICT_ID ) PARALLEL 4 ';
8 execute immediate ' select 1 from dual '
9 into :l1;
10 end;
11 ]'
12 USING OUT l1;
13
14 DBMS_OUTPUT.put_line ('L1 = ' || l1);
15 END;
16 /
L1 = 1
PL/SQL procedure successfully completed.
SQL>
Therefore, maybe it is about your migration tool (which one is it) that doesn't recognize the q-quoting mechanism. If that's so, don't use it and double single quotes instead:
SQL> DECLARE
2 l1 INTEGER;
3 BEGIN
4 EXECUTE IMMEDIATE '
5 begin
6 execute immediate '' CREATE INDEX S_ACCNT_ATT_P1 ON S_ACCNT_ATT ( ROW_ID ) PARALLEL 4 '';
7 execute immediate '' CREATE INDEX S_ACCNT_ATT_U1 ON S_ACCNT_ATT ( PAR_ROW_ID , FILE_NAME , FILE_EXT , CONFLICT_ID ) PARALLEL 4 '';
8 execute immediate '' select 1 from dual ''
9 into :l1;
10 end;
11 '
12 USING OUT l1;
13
14 DBMS_OUTPUT.put_line ('L1 = ' || l1);
15 END;
16 /
PL/SQL procedure successfully completed.
SQL>

ERROR at line 6: PL/SQL: SQL Statement ignored

I am getting an error at line 6: PL/SQL: SQL Statement ignored.
The line with "select count(*) into ecount" gives the error.
Could anyone help me?
My code:
create or replace function empcount(department in table_employee.dept_name%type)
return integer
as
ecount integer;
begin
select count(*) into ecount
from table_employee
where table_employee.dept_name=deparment and salary>500000;
return ecount;
end;
What is obvious is this: department vs. deparment:
create or replace function empcount(department in table_employee.dept_name%type)
^^^^^^^^^^
vvvvvvvvv
where table_employee.dept_name =deparment and salary>500000;
Apart from that, should be OK:
SQL> create or replace function empcount
2 (department in table_employee.dept_name%type)
3 return integer
4 as
5 ecount integer;
6 begin
7 select count(*) into ecount
8 from table_employee
9 where table_employee.dept_name = department --> fixed
10 and salary > 50000;
11 return ecount;
12 end;
13 /
Function created.
SQL> select empcount(20) from dual;
EMPCOUNT(20)
------------
2
SQL>

query to count the table rows using execute immediate

Write a function named get_total_records, to pass the table name as a parameter, and get back the number of records that are contained in the table.
Please test your function with multiple tables.
Which database is it? If Oracle, here's one option:
SQL> CREATE OR REPLACE FUNCTION get_total_records (par_table_name IN VARCHAR2)
2 RETURN NUMBER
3 IS
4 retval NUMBER;
5 BEGIN
6 EXECUTE IMMEDIATE
7 'select count(*) from ' || DBMS_ASSERT.sql_object_name (par_table_name)
8 INTO retval;
9
10 RETURN retval;
11 END;
12 /
Function created.
SQL> SELECT get_total_records ('emp') result FROM DUAL;
RESULT
----------
14
SQL> SELECT get_total_records ('dept') result FROM DUAL;
RESULT
----------
4
SQL> SELECT get_total_records ('does_not_exist') result FROM DUAL;
SELECT get_total_records ('does_not_exist') result FROM DUAL
*
ERROR at line 1:
ORA-44002: invalid object name
ORA-06512: at "SYS.DBMS_ASSERT", line 383
ORA-06512: at "SCOTT.GET_TOTAL_RECORDS", line 6
SQL>

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.

Not able to store values to a nested table in oracle

I am trying to write a function to show values for monthly data according to the selection made by the user in monthly report. Code snippet below is just trying to fetch values in a nested table and once data is loaded successfully in a nested table, I will call the function to display the table. I have tried a few things; but am running into issues while loading data. Below are 2 different SQLs to create this function but both of them are getting same error regarding incorrect values; I have tried a few things but to no avail:
Snippet 1:
/* Formatted on 10/16/2012 8:40:45 AM (QP5 v5.215.12089.38647) */
CREATE OR REPLACE TYPE tempObject AS OBJECT
(
kpiid number,
kpigroup VARCHAR2 (300)
);
CREATE OR REPLACE TYPE tempTable AS TABLE OF tempObject;
CREATE OR REPLACE FUNCTION KPI_HORIZON.Monthly_All_Data (
mainarea IN VARCHAR2)
RETURN tempTable
IS
MonthlyData temptable := temptable ();
n INTEGER := 0;
BEGIN
IF (mainarea = 'ALL')
THEN
FOR r IN (SELECT DISTINCT kpiid, kpigroup
FROM kpi_summary_reporting
WHERE kpifrequency = 'Monthly' AND active_ind = 'Y')
LOOP
monthlydata.EXTEND;
n := n + 1;
monthlydata (n) := tempobject (r.kpiid, r.kpigroup);
END LOOP;
END IF;
RETURN MonthlyData;
END;
Error: [Error] PLS-00306 (26: 29): PLS-00306: wrong number or types of arguments in call to 'TEMPOBJECT'
Snippet2:
/* Formatted on 10/16/2012 8:27:22 AM (QP5 v5.215.12089.38647) */
CREATE OR REPLACE TYPE tempObject AS OBJECT
(
kpiid NUMBER,
kpigroup VARCHAR2 (300)
);
CREATE OR REPLACE TYPE tempTable AS TABLE OF tempObject;
CREATE OR REPLACE FUNCTION KPI_HORIZON.Monthly_All_Data (
mainarea IN VARCHAR2)
RETURN tempTable
AS
MonthlyData temptable := temptable ();
BEGIN
IF (mainarea = 'ALL')
THEN
SELECT DISTINCT ksr.kpiid, ksr.kpigroup
INTO MonthlyData
FROM kpi_summary_reporting ksr
WHERE kpifrequency = 'Monthly' AND active_ind = 'Y';
ELSE
SELECT DISTINCT kpiid, kpigroup
INTO MonthlyData
FROM kpi_summary_reporting;
END IF;
RETURN MonthlyData;
END;
Error: [Error] ORA-00947 (24: 9): PL/SQL: ORA-00947: not enough values
I would do something like this assuming that the data is small enough that it really makes sense to load it entirely into a nested table in the server's PGA. If the data volume is larger, you probably want to use a pipelined table function instead.
Since your nested table is a table of object types, you need to use the object type constructor.
CREATE OR REPLACE FUNCTION KPI_HORIZON.Monthly_All_Data (
mainarea IN VARCHAR2)
RETURN tempTable
IS
MonthlyData temptable;
BEGIN
IF (mainarea = 'ALL')
THEN
SELECT tempObject( kpiid, kpigroup )
BULK COLLECT INTO monthlydata
FROM kpi_summary_reporting
WHERE kpifrequency = 'Monthly'
AND active_ind = 'Y';
END IF;
RETURN MonthlyData;
END;
I'm always dubious when I see a DISTINCT in a query. Do you really expect to get duplicate rows that you need to remove? If not, you'll be much better served removing the DISTINCT as I did above. If you really need the DISTINCT, then your object type would need a MAP or an ORDER method which would complicate the example a bit.
A demonstration of this working
SQL> CREATE OR REPLACE TYPE tempObject AS OBJECT
2 (
3 kpiid NUMBER,
4 kpigroup VARCHAR2 (300)
5 );
6 /
Type created.
SQL> CREATE OR REPLACE TYPE tempTable AS TABLE OF tempObject;
2 /
Type created.
SQL> create table kpi_summary_reporting (
2 kpiid integer,
3 kpigroup varchar2(300),
4 kpifrequency varchar2(30),
5 active_ind varchar2(1)
6 );
Table created.
SQL> insert into kpi_summary_reporting values( 1, 'Foo', 'Monthly', 'Y' );
1 row created.
SQL> ed
Wrote file afiedt.buf
1 CREATE OR REPLACE FUNCTION Monthly_All_Data (
2 mainarea IN VARCHAR2)
3 RETURN tempTable
4 IS
5 MonthlyData temptable;
6 BEGIN
7 IF (mainarea = 'ALL')
8 THEN
9 SELECT tempObject( kpiid, kpigroup )
10 BULK COLLECT INTO monthlydata
11 FROM kpi_summary_reporting
12 WHERE kpifrequency = 'Monthly'
13 AND active_ind = 'Y';
14 END IF;
15 RETURN MonthlyData;
16* END;
17 /
Function created.
SQL> select monthly_all_data( 'ALL' ) from dual;
MONTHLY_ALL_DATA('ALL')(KPIID, KPIGROUP)
--------------------------------------------------------------------------------
TEMPTABLE(TEMPOBJECT(1, 'Foo'))

Resources