i have a type
create or replace TYPE "CUSTOM_DATA" IS TABLE OF VARCHAR2(4000) .
i can assign some values and print to output without problem
declare
cust_data CUSTOM_DATA;
begin
cust_data:=CUSTOM_DATA('A','B','C');
FOR i IN 1 .. cust_data.COUNT
LOOP
DBMS_OUTPUT.put_line (cust_data(i));
END LOOP;
end;
But how can i use cust_data in select clause , because i will assign them to a ref cursor , how can i do this? my code below is not working
SELECT COLUMN_VALUE as val FROM table(cust_data);
Your code works:
DECLARE
cust_data CUSTOM_DATA;
BEGIN
cust_data:=CUSTOM_DATA('A','B','C');
FOR i IN (SELECT COLUMN_VALUE as val FROM table(cust_data))
LOOP
DBMS_OUTPUT.put_line(i.val);
END LOOP;
END;
/
or:
DECLARE
cust_data CUSTOM_DATA;
cur SYS_REFCURSOR;
value VARCHAR2(4000);
BEGIN
cust_data:=CUSTOM_DATA('A','B','C');
OPEN cur FOR
SELECT COLUMN_VALUE as val FROM table(cust_data);
LOOP
FETCH cur INTO value;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.put_line(value);
END LOOP;
END;
/
Which both output:
A
B
C
db<>fiddle here
Related
I have a function called GET_CLIENT_IN_SED(return sys_refcursor), it gives me a list of id numbers(single column). Now, in a procedure, I am trying to loop through each (one by one) of that values and use it for calling a second procedure (it needs a client id parameter).
PROCEDURE GET_ORDINARY_CLIENT;
PROCEDURE GET_ORDINARY_CLIENT_BY_SED
( sed_in IN varchar2, client_sed OUT SYS_REFCURSOR )
IS
ordinary_clients sys_refcursor;
BEGIN
ordinary_clients := GET_CLIENT_IN_SED(sed_in);
for item in ordinary_clients loop
client_sed := client_sed + ordinary_clients(i);
end loop;
END;
As far i could understand you need to do something like :
Function:
This function would take input as number and return a refcursor. Similar to your requirement.
CREATE OR REPLACE FUNCTION get_num_sysrefcur (num IN NUMBER)
RETURN SYS_REFCURSOR
AS
my_cursor SYS_REFCURSOR;
BEGIN
--
OPEN my_cursor FOR
WITH ntable
AS (SELECT 1 ID, 111 AGT, 'ABC' DESCRIP FROM DUAL
UNION ALL
SELECT 2 ID, 222 AGT, 'ABC' DESCRIP FROM DUAL
UNION ALL
SELECT 1 ID, 333 AGT, 'ABC' DESCRIP FROM DUAL)
SELECT AGT FROM ntable WHERE ID = num;
RETURN my_cursor;
END;
/
Block ( In your case Procedure )
-- This anonymous block will loop through the records return from the sys_refcursor. Similiar to you need where you want the second procedure to use the value of sys_refcursor and loop it(You can create procedure in place of this anonymous block).
DECLARE
a NUMBER := 1;
TYPE ta IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
b ta;
x SYS_REFCURSOR;
BEGIN
x := get_num_sysrefcur (a);
fetch x bulk collect into b;
for i in 1..b.count
loop
-- Displaying the result of the ref_cursor.
DBMS_OUTPUT.put_line (b(i));
end loop;
END;
To loop through a ref cursor is not like looping through an array or table which explains why your FOR...LOOP is not working.
In short, instead of a collection, the ref_cursor is more of a "pointer" or an "iterator" over a collection. In this other question you will find a quite clear example of iterating through a ref_cursor using FETCH.
How to use record to loop a ref cursor?
An example with your data would look like this :
PROCEDURE GET_ORDINARY_CLIENT_BY_SED(sed_in IN VARCHAR2,
client_sed OUT SYS_REFCURSOR) IS
ordinary_clients SYS_REFCURSOR;
clt NUMBER; -- assuming your cursor contains strictly numbers
BEGIN
ordinary_clients := GET_CLIENT_IN_SED(sed_in);
LOOP
FETCH ordinary_clients
INTO clt;
EXIT WHEN ordinary_clients%NOTFOUND;
dbms_output.put_line(clt);
-- do some other things here with your number
END LOOP;
END;
Getting error while using two cursors
[Error] PLS-00103 (45: 48): PLS-00103: Encountered the symbol
"TX_COM_LOCATION" when expecting one of the following:
:= . ( # % ;
The symbol ":=" was substituted for
"TX_COM_LOCATION" to continue.
Please help
CREATE OR REPLACE PROCEDURE COM_LOCATION_TXM
IS
BEGIN
DECLARE CURSOR TXM_COM_LOCATION IS SELECT col1,col2,col3 from TBL_SAR_SALAS_1 A;
CURSOR TX_COM_LOCATION is select col1,col2,col3 from TBL_LOCALES B;
TMP_TXM TXM_COM_LOCATION%ROWTYPE;
TMP_TXM TX_COM_COCATION%ROWTYPE;
Begin
IF NOT TXM_COM_LOCATION%ISOPEN
THEN OPEN TXM_COM_LOCATION;
END IF;
FETCH TXM_COM_LOCATION INTO TMP_TXM;
EXIT
WHEN TXM_COM_LOCATION%NOTFOUND;
IF NOT TX_COM_LOCATION%ISOPEN
THEN
OPEN TXCOM_LOCATION;
END IF;
LOOP FETCH TX_COM_LOCATION INTO TMP_TX; EXIT WHEN TX_COM_LOCATION%NOTFOUND;
BEGIN Insert statement()
END;
END LOOP;
END LOOP;
commit;
END;
END COM_LOCATION_TXM ;
Check this:
CREATE OR REPLACE PROCEDURE COM_LOCATION_TXM IS BEGIN DECLARE CURSOR TXM_COM_LOCATION IS SELECT col1, col2, col3 FROM TBL_SAR_SALAS_1 A; CURSOR TX_COM_LOCATION IS SELECT col1, col2, col3 FROM TBL_LOCALES B; TMP_TXM TXM_COM_LOCATION%ROWTYPE; TMP_TXM TX_COM_LOCATION%ROWTYPE; BEGIN IF NOT TXM_COM_LOCATION%ISOPEN THEN OPEN TXM_COM_LOCATION; END IF; LOOP FETCH TXM_COM_LOCATION INTO TMP_TXM; EXIT WHEN TXM_COM_LOCATION%NOTFOUND; IF NOT TX_COM_LOCATION%ISOPEN THEN OPEN TXCOM_LOCATION; END IF; LOOP FETCH TX_COM_LOCATION INTO TMP_TX; EXIT WHEN TX_COM_LOCATION%NOTFOUND; BEGIN NULL; -- REPLACE NULL WITH INSERT STATEMENT END; END LOOP; END LOOP; COMMIT; END; END COM_LOCATION_TXM;
I am trying to fetch data from a table(employee) with cursor and save to asso. array. However, fetching data with cursor to a record is more straight-forward and it is troublesome to convert a record to an assoc array(arr). Code below is what I am trying to fetch data to a assoc array and improvement is needed. Or any approaches other than cursor? Thank you.
DECLARE
TYPE AssoArray IS TABLE OF varchar2(30) INDEX BY varchar2(30);
arr AssoArray;
table_rec employee%rowtype;
CURSOR cur IS
SELECT * FROM employee;
BEGIN
OPEN cur;
LOOP
FETCH cur into table_rec;
EXIT WHEN cur%notfound;
-- how to improve the code in the section below,
arr('col1') := table_rec.col1;
arr('col2') := table_rec.col2;
arr('col3') := table_rec.col3;
...
arr('col50') := table_rec.col50;
-- end of section
-- do sth
END LOOP;
END;
Could you explain what do you want to get. But I see in your code arr AssoArray will contain only one last fetched row and will be rewrite each cycle. Are you realy need it? My suggestion is you want to get some rows of table as associated array. If its true, you may create the array as table of rowtype and (for example) indexed it by id(if you ID-s is integer).
TYPE AssoArray IS TABLE OF employee%rowtype INDEX BY PLS_INTEGER;
For example
create table tmp_table_01 as select * from all_objects where rownum < 11;
DECLARE
TYPE AssoArray IS TABLE OF tmp_table_01%rowtype INDEX BY varchar2(30);
arr AssoArray;
table_rec tmp_table_01%rowtype;
CURSOR cur IS
SELECT * FROM all_objects where rownum < 20;
BEGIN
OPEN cur;
LOOP
FETCH cur into table_rec;
EXIT WHEN cur%notfound;
-- how to improve the code in the section below,
arr(table_rec.object_name) := table_rec;
-- end of section
dbms_output.put_line(table_rec.object_name ||' '||arr(table_rec.object_name).object_id );
-- do sth
END LOOP;
END;
EDIT:
If you want to make comraisons by table structure you may use Dynamic SQL. NExt code get data from table TMP_TABLE_01 sort it by object_id, compare neighbour rows, and return count of difference row.
DECLARE
l_sql varchar2(32767);
TYPE t_cols_type IS table of varchar2(30);
l_cols t_cols_type ;
l_result number;
BEGIN
SELECT c.COLUMN_NAME
BULK COLLECt INTO l_cols
FROM user_tab_cols c
WHERE c.TABLE_NAME = 'TMP_TABLE_01';
l_sql := 'WITH s AS (SELECT t.*, row_number() OVER (ORDER BY object_id) AS rn FROM TMP_TABLE_01 t)
SELECT count(*)
FROM s
JOIN s sub_s ON s.rn = sub_s.rn - 1
where 1=0 ';
FOR i IN 1 .. l_cols.last LOOP
l_sql := l_sql || ' OR decode(s.'||l_cols(i)||',sub_s.'||l_cols(i)||',1,0) = 0';
END LOOP;
dbms_output.put_line(l_sql);
EXECUTE IMMEDIATE l_sql
INTO l_result ;
dbms_output.put_line('count of conseuence different rows ' ||l_result);
END;
I'm a bit stuck. I have a table with a list of database names. I want to query for the database name and then query this database to return details from its "systemtable".
I've been trying to use 2 cursors but its not quite working out for me (just can't find the syntax), any pointers/help would be appreciated.
declare
cursor c_dbNames is select dbname
from DB_INFO order by name ASC;
v_curr_dbname VARCHAR2(60);
begin
open c_dbNames;
LOOP
FETCH c_dbNames into v_curr_dbname;
EXIT WHEN c_dbnames%NOTFOUND;
begin
cursor c_dbDetails is select value
from SYSTEMTABLE#'||v_curr_dbname||' order by name ASC;
v_curr_detail VARCHAR2(60);
open c_dbDetails;
LOOP
FETCH c_dbDetails into v_curr_detail;
EXIT WHEN c_dbDetails%NOTFOUND;
htp.p('<tr><th>'||v_curr_detail||'</th></tr>');
END LOOP;
close c_dbDetails;
end;
END LOOP;
close c_dbnames;
end;
You have to adjust it a little:
declare
cursor c_dbNames is
select 'dual' dbname from dual union all
select 'dual' dbname from dual union all
select 'dual' dbname from dual
order by dbname ASC;
v_curr_dbname VARCHAR2(60);
begin
open c_dbNames;
LOOP
FETCH c_dbNames into v_curr_dbname;
EXIT WHEN c_dbnames%NOTFOUND;
DECLARE
v_cursor integer;
v_rows integer;
v_curr_detail char(20);
begin
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, 'select ''c_dbDetails'' c_dbDetails FROM ' || v_curr_dbname, DBMS_SQL.NATIVE);
DBMS_SQL.DEFINE_COLUMN_CHAR(v_cursor, 1, v_curr_detail, 20);
v_rows := DBMS_SQL.EXECUTE(v_cursor);
loop
if DBMS_SQL.FETCH_ROWS(v_cursor) = 0 then
exit;
end if;
DBMS_SQL.COLUMN_VALUE_CHAR(v_cursor, 1, v_curr_detail);
DBMS_OUTPUT.PUT_LINE('<tr><th>' || v_curr_detail ||'</th></tr>');
end loop;
DBMS_SQL.CLOSE_CURSOR(v_cursor);
end;
END LOOP;
close c_dbnames;
end;
declare
cursor databases_c is
-- put your database links here
select 'XXX' as dbname from dual union
select 'YYY' from dual;
v_global_name varchar2(4000);
begin
for v_dbname in databases_c loop
-- query the database details
execute immediate
'select global_name from global_name#' || v_dbname.dbname
into v_global_name;
dbms_output.put_line(v_global_name);
end loop;
end;
/
Output:
SQL> #so27.sql
XXX
YYY
PL/SQL procedure successfully completed.
SQL>
i m migrating my following plsql function from oracle into mssql 2008 but dont know how to convert cursor in a while loop.Could u help?
CREATE OR REPLACE function f_genel_iskonto (p_ID_MUSTERI_SIRKET in number, p_BILGI_TIP in NUMBER)
return number
is
v_iskonto number;
begin
v_iskonto:=null;
for c in (
SELECT an.mt_iskonto_oran, an.aktif, an.id_anlasma
FROM lu_anlasma an
WHERE an.id_musteri_sirket = p_ID_MUSTERI_SIRKET AND an.id_durum = 9
AND (TRUNC (SYSDATE) BETWEEN an.baslangic AND an.bitis)
ORDER BY an.baslangic DESC
) loop
if p_BILGI_TIP=1 then
v_iskonto:=c.mt_iskonto_oran;
end if;
if p_BILGI_TIP=2 then
v_iskonto:=c.aktif;
end if;
if p_BILGI_TIP=3 then
v_iskonto:=c.id_anlasma;
end if;
exit;
end loop;
return v_iskonto;
exception
when others then
return null;
end;
You don't have to convert a cursor/loop in this case as it only looks at the first record.
create function f_genel_iskonto (#p_ID_MUSTERI_SIRKET int, #p_BILGI_TIP int)
returns int
as
begin
declare #result int
SELECT top 1
#result = case #p_BILGI_TIP
when 1 then an.mt_iskonto_oran
when 2 then an.aktif
when 3 then an.id_anlasma
end
FROM lu_anlasma an
WHERE an.id_musteri_sirket = #p_ID_MUSTERI_SIRKET AND an.id_durum = 9
AND (current_timestamp BETWEEN an.baslangic AND an.bitis)
ORDER BY an.baslangic DESC
return #result
end
And you will probably need to tweak the date comparison to do what you want.