i m using Oracle 9i.
I m fetching data from a cursor into an array :
FETCH contract_cur
BULK COLLECT INTO l_contract ;
But now i want to "convert" this l_contract into a CLOB variable l_clob
Is there an easy way to do that?
Or otherwise, how do i convertthe rows from a SELECT statement into one single CLOB Variable ?
thanks
EDIT : i forgot to mention its an array of %ROWTYPE, not just one column.
What an ugly thing to do.
Is it all character data, or do you have numeric and/or date/time values in there too ? If so what format do you want to use for those datatypes when you convert them to strings.
You also may need to think about field and record delimiters.
Have you considered XML ?
declare
v_clob clob;
v_xml xmltype;
begin
select xmlagg(XMLELEMENT("test",xmlforest(id,val)))
into v_xml
from test;
select v_xml.getclobval
into v_clob
from dual;
dbms_output.put_line(v_clob);
end;
/
you can loop through your array and build the CLOB as you go:
SQL> DECLARE
2 TYPE tab_vc IS TABLE OF VARCHAR2(4000);
3 l_contract tab_vc;
4 l_clob CLOB;
5 BEGIN
6 dbms_lob.createtemporary (l_clob, TRUE);
7 SELECT to_char(dbms_random.STRING('a', 1000)) BULK COLLECT
8 INTO l_contract
9 FROM dual
10 CONNECT BY LEVEL <= 100;
11 FOR i IN 1..l_contract.count LOOP
12 dbms_lob.writeappend(l_clob,
13 length(l_contract(i)),
14 l_contract(i));
15 END LOOP;
16 -- your code here
17 dbms_lob.freetemporary(l_clob);
18 END;
19 /
PL/SQL procedure successfully completed
If you don't use l_contract for anything else you can build the CLOB directly from the cursor loop without the array step, it will save memory and will probably be faster:
SQL> DECLARE
2 l_clob CLOB;
3 BEGIN
4 dbms_lob.createtemporary (l_clob, TRUE);
5 FOR cc IN ( SELECT to_char(dbms_random.STRING('a', 1000)) txt
6 FROM dual
7 CONNECT BY LEVEL <= 100) LOOP
8 dbms_lob.writeappend(l_clob,
9 length(cc.txt),
10 cc.txt);
11 END LOOP;
12 -- your code here
13 dbms_lob.freetemporary(l_clob);
14 END;
15 /
PL/SQL procedure successfully completed
Related
I'm trying to send variable schema name to cursor via procedure input
Here is my lame try, but you can see what I want to do:
CREATE OR REPLACE PROCEDURE HOUSEKEEPING
(SCHEMANAME in varchar2)
IS
CURSOR data_instances IS select table_name
from SCHEMANAME.table_name where TYPE='PERMANENT' and rownum<200 ;
BEGIN
DBMS_OUTPUT.PUT_LINE(SCHEMANAME);
END;
/
it throws expected
PL/SQL: ORA-00942: table or view does not exist
is there lawful way to make schema name work as variable? thanks
There is a way; you'll need some kind of dynamic SQL because you can't use schema (or object) names like that. For example, you could use refcursor instead.
Sample table:
SQL> create table table_name as
2 select 'EMP' table_name, 'PERMANENT' type from dual union all
3 select 'DEPT' , 'TEMPORARY' from dual union all
4 select 'BONUS' , 'PERMANENT' from dual;
Table created.
Procedure; note the way I composed SELECT statement first (so that I could display it and check whether it is correct), and then used it in OPEN. Loop is here to ... well, loop through the cursor. I'm just displaying table names I found - you'd probably do something smarter.
SQL> create or replace procedure housekeeping (par_schemaname in varchar2)
2 is
3 l_str varchar2(500);
4 l_rc sys_refcursor;
5 l_table_name varchar2(30);
6 begin
7 l_str := 'select table_name from ' ||
8 dbms_assert.schema_name(upper(par_schemaname)) ||
9 '.table_name where type = ''PERMANENT'' and rownum < 200';
10 open l_rc for l_str;
11
12 loop
13 fetch l_rc into l_table_name;
14 exit when l_rc%notfound;
15
16 dbms_output.put_line(l_table_name);
17 end loop;
18 close l_rc;
19 end;
20 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec housekeeping('SCOTT');
EMP
BONUS
PL/SQL procedure successfully completed.
SQL>
Can someone please help me? Why do I get this error "INTO list is of wrong type"?
DECLARE
TYPE v_dept_table IS TABLE OF DEPARTMENTS.DEPARTMENT_NAME%TYPE INDEX BY PLS_INTEGER;
v_record_dept_table v_dept_table;
v_loop_count NUMBER := 10;
v_dept_no NUMBER := 1;
BEGIN
FOR v_dept_no IN 1..v_loop_count LOOP
SELECT DEPARTMENTS.DEPARTMENT_NAME
INTO v_record_dept_table
FROM DEPARTMENTS
WHERE DEPARTMENT_ID = v_dept_no;
v_dept_no := v_dept_no + 1;
INSERT
INTO v_dept_table
VALUES v_record_dept_table;
END LOOP;
FOR indx IN NVL (v_dept_table.FIRST, 0) .. NVL (v_dept_table.LAST, -1)
LOOP
DBMS_OUTPUT.PUT_LINE(v_dept_table(indx));
END LOOP;
END;
ORA-06550: line 16, column 14:
PLS-00597: expression 'V_RECORD_DEPT_TABLE' in the INTO list is of wrong type
Why is it a wrong type? I'm using the hr schema from Oracle
Not exactly like that; you've done several things wrong. I tried to fix your code (with as little modifications as possible); have a look, read comments I wrote, compare it to your code. Side note: I used Scott's DEPT table as I don't have your DEPARTMENTS.
SQL> set serveroutput on
SQL> DECLARE
2 TYPE v_dept_table IS TABLE OF dept.dname%TYPE
3 INDEX BY PLS_INTEGER;
4
5 v_record_dept_table v_dept_table;
6
7 v_loop_count NUMBER := 10;
8 v_dept_no NUMBER := 1;
9 BEGIN
10 FOR v_dept_no IN 1 .. v_loop_count
11 LOOP
12 BEGIN
13 SELECT dname
14 INTO v_record_dept_table (v_dept_no) --> you're missing "(v_dept_no)"
15 FROM dept
16 WHERE deptno = v_dept_no;
17 -- Don't manually increment FOR loop variable; Oracle does it itself
18 -- v_dept_no := v_dept_no + 1;
19
20 -- You can't insert into "type"; besides, you've already inserted into V_RECORD_DEPT_TABLE.
21 -- INSERT INTO v_dept_table VALUES v_record_dept_table;
22 EXCEPTION
23 WHEN NO_DATA_FOUND
24 THEN
25 NULL;
26 END;
27 END LOOP;
28
29 -- loop through V_RECORD_DEPT_TABLE (collection), not V_DEPT_TABLE (type). No need for NVL.
30 FOR indx IN NVL (v_record_dept_table.FIRST, 0) ..
31 NVL (v_record_dept_table.LAST, -1)
32 LOOP
33 DBMS_OUTPUT.PUT_LINE (v_record_dept_table (indx));
34 END LOOP;
35 END;
36 /
ACCOUNTING
PL/SQL procedure successfully completed.
SQL>
Alternatively, see whether this helps. I used built-in type (sys.odcivarchar2list) and BULK COLLECT INTO (performs better).
SQL> DECLARE
2 v_record_dept_table SYS.odcivarchar2list;
3 BEGIN
4 SELECT dname
5 BULK COLLECT INTO v_record_dept_table
6 FROM dept;
7
8 FOR indx IN v_record_dept_table.FIRST .. v_record_dept_table.LAST
9 LOOP
10 DBMS_OUTPUT.PUT_LINE (v_record_dept_table (indx));
11 END LOOP;
12 END;
13 /
ACCOUNTING
RESEARCH
SALES
OPERATIONS
PL/SQL procedure successfully completed.
SQL>
Why do we use execute immediate command in PL/SQL?
I'm looking at some procedures written by a previous colleague, and I see that the person has used execute immediate a lot many time to log the progress of the procedure and also when truncating the tables.
My question is why would he do so? Can we not just truncate tables just like that in pl/sql proc?
Oracle wouldn't allow execution of DDL inside executable block. This is not allowed
begin
alter table . . .
end;
Originally Oracle had SYS.DBMS_DDL package to do this job. But it was cumbersome to use, so oracle introduced execute immediate circa v9.
Apart from executing DDL from PL/SQL (which you've already been told), execute immediate is used to run dynamic SQL. What would that be? Creating statements that depend on information that is not known at the time you're creating the PL/SQL procedure. For example, selecting from several tables in your schema:
SQL> set serveroutput on
SQL> declare
2 l_str varchar2(1000);
3 l_cnt number;
4 begin
5 for cur_r in (select table_name from user_tables
6 where table_name in ('EMP', 'DEPT', 'BONUS')
7 )
8 loop
9 l_str := 'select count(*) from ' || cur_r.table_name;
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(cur_r.table_name ||': '|| l_cnt);
12 end loop;
13 end;
14 /
BONUS: 0
DEPT: 4
EMP: 14
PL/SQL procedure successfully completed.
SQL>
Similarly, you might create a function that uses table (or column) names dynamically, e.g.
SQL> create or replace function f_cnt (par_table_name in varchar2)
2 return number
3 is
4 l_str varchar2(1000);
5 l_cnt number;
6 begin
7 l_str := 'select count(*) from ' || dbms_assert.sql_object_name(par_table_name);
8 execute immediate l_str into l_cnt;
9 return l_cnt;
10 end;
11 /
Function created.
SQL> select f_cnt('emp') from dual;
F_CNT('EMP')
------------
14
SQL>
DECLARE
l_rcursor SYS_REFCURSOR;
BEGIN
OPEN l_rcursor FOR SELECT * FROM all_users;
dbms_output.put_line(l_rcursor%ROWCOUNT);
END;
This is long code so i cant use the cursor inside the declare portion. Here i need the count of rows fetched. I cant use rowtype because it is a join query.
Well, you can not.
There's a way to find out whether cursor contains anything (using the %NOTFOUND attribute), such as
SQL> declare
2 l_rcursor sys_refcursor;
3 l_rec all_users%rowtype;
4 begin
5 open l_rcursor for select * From all_users
6 where 1 = 2; --> will cause nothing to be returned
7
8 -- check whether there is (or is not) anything there
9 fetch l_rcursor into l_rec;
10 if l_rcursor%notfound then
11 dbms_output.put_line('There''s nothing there');
12 end if;
13 end;
14 /
There's nothing there
PL/SQL procedure successfully completed.
SQL>
but you can't know how many rows it'll return, which means that you'll have to count number of rows elsewhere (probably code that uses what you wrote in your question).
Possible duplicate to counting rows from a cursor in pl/sql
You can also just use COUNT in your cursor query to solve your problem, sample code below
DECLARE
l_rcursor SYS_REFCURSOR;
v_count NUMBER;
BEGIN
OPEN l_rcursor FOR SELECT COUNT(1) FROM (SELECT * FROM all_users);
-- OR
-- OPEN l_rcursor FOR SELECT COUNT(1) FROM all_users;
FETCH l_rcursor INTO v_count;
dbms_output.put_line(v_count);
END;
/
Can I access a cursor's column dynamically? I mean by name? something like this:
declare
v_cursor := select * from emp;
begin
FOR reg IN v_cursor LOOP
dbms_output.put_line(**reg['column_name_as_string']**);
end loop;
end;
I know the bold part is not PL/SQL, but I'm looking for something like that and can't find it anywhere.
You can use the package DBMS_SQL to create and access cursors with dynamic queries.
However it's not really straightforward to access a column by name because the DBMS_SQL package uses positioning and in a dynamic query we may not know the order of the columns before the execution.
Furthermore, in the context of this question, it appears that we may not know which column we want to display at compile time, we will assume that the column we want to display is given as a parameter.
We can use DBMS_SQL.describe_columns to analyze the columns of a SELECT query after it has been parsed to build a dynamic mapping of the columns. We will assume that all columns can be cast into VARCHAR2 since we want to display them with DBMS_OUTPUT.
Here's an example:
SQL> CREATE OR REPLACE PROCEDURE display_query_column(p_query VARCHAR2,
2 p_column VARCHAR2) IS
3 l_cursor INTEGER;
4 l_dummy NUMBER;
5 l_description_table dbms_sql.desc_tab3;
6 TYPE column_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(32767);
7 l_mapping_table column_map_type;
8 l_column_value VARCHAR2(4000);
9 BEGIN
10 l_cursor := dbms_sql.open_cursor;
11 dbms_sql.parse(l_cursor, p_query, dbms_sql.native);
12 -- we build the column mapping
13 dbms_sql.describe_columns3(l_cursor, l_dummy, l_description_table);
14 FOR i IN 1 .. l_description_table.count LOOP
15 l_mapping_table(l_description_table(i).col_name) := i;
16 dbms_sql.define_column(l_cursor, i, l_column_value, 4000);
17 END LOOP;
18 -- main execution loop
19 l_dummy := dbms_sql.execute(l_cursor);
20 LOOP
21 EXIT WHEN dbms_sql.fetch_rows(l_cursor) <= 0;
22 dbms_sql.column_value(l_cursor, l_mapping_table(p_column), l_column_value);
23 dbms_output.put_line(l_column_value);
24 END LOOP;
25 dbms_sql.close_cursor(l_cursor);
26 END;
27 /
Procedure created
We can call this procedure with a query known only at run-time:
SQL> set serveroutput on
SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'ENAME');
SMITH
ALLEN
WARD
JONES
PL/SQL procedure successfully completed
SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'EMPNO');
7369
7499
7521
7566
PL/SQL procedure successfully completed
Use caution with dynamic SQL: it has the same privileges as the user and can therefore execute any DML and DDL statement allowed for this schema.
For instance, the above procedure could be used to create or drop a table:
SQL> exec display_query_column('CREATE TABLE foo(id number)', '');
begin display_query_column('CREATE TABLE foo(id number)', ''); end;
ORA-01003: aucune instruction analysée
ORA-06512: à "SYS.DBMS_SQL", ligne 1998
ORA-06512: à "APPS.DISPLAY_QUERY_COLUMN", ligne 13
ORA-06512: à ligne 1
SQL> desc foo
Name Type Nullable Default Comments
---- ------ -------- ------- --------
ID NUMBER Y
It's probably easiest to make the query dynamic if you can.
DECLARE
v_cursor SYS_REFCURSOR;
dynamic_column_name VARCHAR2(30) := 'DUMMY';
column_value VARCHAR2(32767);
BEGIN
OPEN v_cursor FOR 'SELECT ' || dynamic_column_name || ' FROM dual';
LOOP
FETCH v_cursor INTO column_value;
EXIT WHEN v_cursor%NOTFOUND;
dbms_output.put_line( column_value );
END LOOP;
CLOSE v_cursor;
END;
If you really want to have a hardcoded SELECT * and dynamically select a column from that by name, I think you could do that using DBMS_SQL as Vincent suggests, but it will be somewhat more complex.
You mean something like:
declare
cursor sel_cur is
select * from someTable;
begin
for rec in sel_cur
loop
dbms_output.put_line('col1: ' || rec.col1);
end loop;
end;