oracle function and cursor using dynamic table name - oracle11g

IN my oracle database i want to create a function or procedure with cursor which will use dynamic table name.here is my code.
CREATE OR REPLACE Function Findposition ( model_in IN varchar2,model_id IN number) RETURN number IS cnumber number;
TYPE c1 IS REF CURSOR;
c2 c1;
BEGIN
open c2 FOR 'SELECT id,ROW_NUMBER() OVER ( ORDER BY id) AS rownumber FROM '||model_in;
FOR employee_rec in c2
LOOP
IF employee_rec.id=model_id then
cnumber :=employee_rec.rownumber;
end if;
END LOOP;
close c2;
RETURN cnumber;
END;
help me to solve this problem.IN

There is no need to declare a c1 type for a weakly typed ref cursor. You can just use the SYS_REFCURSOR type.
You can't mix implicit and explicit cursor calls like this. If you are going to OPEN a cursor, you have to FETCH from it in a loop and you have to CLOSE it. You can't OPEN and CLOSE it but then fetch from it in an implicit cursor loop.
You'll have to declare a variable (or variables) to fetch the data into. I declared a record type and an instance of that record but you could just as easily declare two local variables and FETCH into those variables.
ROWID is a reserved word so I used ROWPOS instead.
Putting that together, you can write something like
SQL> ed
Wrote file afiedt.buf
1 CREATE OR REPLACE Function Findposition (
2 model_in IN varchar2,
3 model_id IN number)
4 RETURN number
5 IS
6 cnumber number;
7 c2 sys_refcursor;
8 type result_rec is record (
9 id number,
10 rowpos number
11 );
12 l_result_rec result_rec;
13 BEGIN
14 open c2 FOR 'SELECT id,ROW_NUMBER() OVER ( ORDER BY id) AS rowpos FROM '||model_in;
15 loop
16 fetch c2 into l_result_rec;
17 exit when c2%notfound;
18 IF l_result_rec.id=model_id
19 then
20 cnumber :=l_result_rec.rowpos;
21 end if;
22 END LOOP;
23 close c2;
24 RETURN cnumber;
25* END;
SQL> /
Function created.
I believe this returns the result you expect
SQL> create table foo( id number );
Table created.
SQL> insert into foo
2 select level * 2
3 from dual
4 connect by level <= 10;
10 rows created.
SQL> select findposition( 'FOO', 8 )
2 from dual;
FINDPOSITION('FOO',8)
---------------------
4
Note that from an efficiency standpoint, you'd be much better off writing this as a single SQL statement rather than opening a cursor and fetching every row from the table every time. If you are determined to use a cursor, you'd want to exit the cursor when you've found the row you're interested in rather than continuing to fetch every row from the table.
From a code clarity standpoint, many of your variable names and data types seem rather odd. Your parameter names seem poorly chosen-- I would not expect model_in to be the name of the input table, for example. Declaring a cursor named c2 is also problematic since it is very non-descriptive.

You can do this, you don't need loop when you are using dynamic query
CREATE OR REPLACE Function Findposition(model_in IN varchar2,model_id IN number)
RETURN number IS
cnumber number;
TYPE c1 IS REF CURSOR;
c2 c1;
BEGIN
open c2 FOR 'SELECT rownumber
FROM (
SELECT id,ROW_NUMBER() OVER ( ORDER BY id) AS rownumber
FROM '||model_in || '
) WHERE id = ' || model_id;
FETCH c2 INTO cnumber;
close c2;
return cnumber;
END;

Related

Use cursor in LOOP in new QUERY

"I missing the forest through the trees..."
I want to query each column of a table which I retrieve in a FOR LOOP, but the inner query doesn't return the right thing.
Seems that the inner query not use the current column_name.
DECLARE
v_max_TS TIMESTAMP;
BEGIN
FOR cols IN (SELECT column_name FROM all_tab_cols WHERE table_name = '<tablename>')
LOOP
SELECT
MAX(CURR_TIMESTAMP) INTO v_max_TS
FROM <tablename>
WHERE cols.column_name IS NOT NULL
ORDER BY TO_TIMESTAMP(CURR_TIMESTAMP,'MM/DD/YYYY HH24:MI:SS') DESC;
dbms_output.put_line(cols.column_name || ' ' || v_max_TS);
END LOOP;
END;
Apart from the fact that your query doesn't make much sense (as Boneist wrote as a comment), that won't work as you need to use dynamic SQL (execute immediate) for such a purpose.
Here's an example based on Scott's schema. Have a look, adjust it if necessary.
SQL> set serveroutput on
SQL> declare
2 l_str varchar2(200); -- will hold the SELECT statement
3 v_max varchar2(30);
4 begin
5 for cols in (select column_name
6 from all_tab_cols
7 where table_name = 'DEPT'
8 )
9 loop
10 l_str := 'select max(' || cols.column_name ||') from dept';
11 execute immediate l_str into v_max;
12 dbms_output.put_line(cols.column_name ||': '|| v_max);
13 end loop;
14 end;
15 /
DEPTNO: 40
DNAME: SALES
LOC: NEW YORK
PL/SQL procedure successfully completed.
SQL>

Errors in triggers

This is my trigger:
CREATE OR REPLACE TRIGGER trg_CheckStaffID
BEFORE INSERT ON ASSIGN
FOR EACH ROW
BEGIN
DECLARE id integer := 0;
SET id := (select count(*) from (select staffid from staff where staffid ='T2');
IF (id=0) THEN
RAISE_APPLICATION_ERROR(-20000,'Please Enter A Valid Staff ID');
END IF;
END;
/
And this is the error message I get:
PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:
( - + case mod new not null
continue avg count current exists max min prior sql stddev
sum variance execute forall merge time timestamp interval
date
pipe
PLS-00103: Encountered the symbol "IF"
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
end not pragma final instantiable order overriding static
member constructor map
Invalid syntax on different places; DECLARE section should be before BEGIN. There's no SET command in PL/SQL.
Here's code that compiles; whether it does what you meant, can't tell. (I'm creating dummy tables, just to make sure that trigger creation wouldn't fail).
SQL> create table assign (id number);
Table created.
SQL> create table staff (staffid varchar2(2));
Table created.
SQL> create or replace trigger trg_checkstaffid
2 before insert on assign
3 for each row
4 declare
5 id integer := 0;
6 begin
7 select count(*)
8 into id
9 from (select staffid
10 from staff
11 where staffid ='T2');
12
13 if id = 0 then
14 raise_application_error(-20000, 'Please Enter A Valid Staff ID');
15 end if;
16 end;
17 /
Trigger created.
SQL>

Extract range of elements from plsql Associative array

I am currently on oracle 11.2. Below is a snippet of the code.I want to extract range of elements from record type on every page number passed.
Rather than in the query itself I want to extract from the table type.
suppose the collection is filled with 13 records
page passed with 1 should give elements from 1 to 5
page =2 -> 6 to 10
page =3 -> 11 to 13
I don't want to put page logic in the select statement.
I am not getting the correct output when i pass page 2 and on wards.
I don't have the exact code right now,but when I go to office tomorrow morning,I will update the correct code which is inside the loop.
create or replace procedure p1 (page number) is
TYPE rec_typ IS RECORD (col1 VARCHAR2(5),col2 VARCHAR2(50),col3
number(10));
TYPE rec_tab IS TABLE OF rec_typ INDEX BY BINARY_INTEGER;
t_tab rec_tab ;
f_tab rec_tab ;
n number :=0;
BEGIN
Select * bulk collect into t_tab from test;
For j in p1*5-4..p1*5
LOOP
if t_tab.exists(j) then
n:= n+1;
f_tab.extend;
f_tab(n) :=t_tab(j);
end if;
END LOOP;
END;
use LIMIT Option it will help you!!
refer : http://www.dba-oracle.com/plsql/t_plsql_limit_clause.htm
Sample query : in this code you can pass 13 in your parameter.
create or replace procedure p1 (page number) is
TYPE rec_typ IS RECORD (col1 VARCHAR2(5),col2 VARCHAR2(50),col3 number(10));
TYPE rec_tab IS TABLE OF rec_typ INDEX BY BINARY_INTEGER;
t_tab rec_tab ;
f_tab rec_tab ;
n number :=0;
j number :=1;
CURSOR C IS
Select * bulk collect into t_tab from test;
BEGIN
OPEN C;
LOOP
FETCH C BULK_COLLECT INTO t_tab LIMIT 5;
EXIT WHEN L_PF.COUNT=0;
if t_tab.exists(j) then
n:= n+1;
f_tab.extend;
f_tab(n) :=t_tab(j);
end if;
j:=j+1;
END LOOP;
CLOSE C;
END;
Note: Its only for Sample code . Engage this code with your logic. Hope it will help you. if its helpful to you, click useful up tick button, which is left side of this answer.
Instead of this collection you can use ROWNUM and then use pagination concept here. Hope this below snippet helps.
CREATE OR REPLACE
PROCEDURE p1(
page NUMBER)
IS
TYPE rec_typ
IS
RECORD
(
col1 VARCHAR2(5),
col2 VARCHAR2(50),
col3 NUMBER(10));
TYPE rec_tab
IS
TABLE OF rec_typ INDEX BY BINARY_INTEGER;
t_tab rec_tab ;
LV_SQL VARCHAR2(32676);
lv_cond VARCHAR2(32676);
BEGIN
LV_COND:=
CASE
WHEN PAGE = 1 THEN
' AND a.rn BETWEEN 1 AND 5 '
WHEN PAGE =2 THEN
' AND a.rn BETWEEN 6 AND 10 '
WHEN PAGE = 3 THEN
' AND a.rn BETWEEN 11 AND 15 '
ELSE
''
END;
LV_SQL:= 'SELECT
a.col1,
a.col2,
a.col3
FROM
(SELECT T.*,ROWNUM RN FROM TEST T)A
WHERE 1 = 1 '||lv_cond;
EXECUTE IMMEDIATE lv_sql BULK COLLECT INTO t_tab;
FOR z IN t_tab.FIRST..t_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(t_tab(z).col1||' '||t_tab(z).col2||' '||t_tab(z).col1);
END LOOP;
END;
/

how to prevent duplicate values while using inner for loop in oracle plsql?

I am passing c1 value as param to the c2,c3 cursor's , so i am getting duplicate values and how to write better than this code using plsql code?
Declare
cursor cur1;
cursor cur2
is
select * from
where param=c1.param;
cursor cur3
is
select * from
where param=c1.param;
Begin
for c1 loop
for c2(c1.param)
dbms_output(deptno||' '||dname);
for c3(c1.param)
dbms_output(deptno||' '||dname);
end loop;
end loop;
end loop;
End;
So , i am getting duplicate values
deptno dname
10 a
20 b
30 c
10 a
20 b
30 c
Expected output as
deptno dname
10 a
20 b
30 c
can you please help me?
Sounds like you may want a UNION:
Declare
cursor cur1;
cursor cur2 is
select * from X
where param=c1.param
union
select * from Y
where param=c1.param;
Begin
for c1 loop
for c2(c1.param)
dbms_output(deptno||' '||dname);
end loop;
end loop;
End;
(I haven't fixed your invalid code above - presumably your real code is correct or it wouldn't have run at all.)
You probably don't need even 2 cursors, you could do something like:
Declare
cursor cur is
select * from X
where param in (select ...)
union
select * from Y
where param in (select ...)
Begin
for c2 in cur loop
dbms_output(deptno||' '||dname);
end loop;
End;
you can use inner join to avoid cursor looping.
use distinct keyword to get distinct value.
user of rownum keword to get only one row.
eliminate duplicate by group by clause.

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