how to write loop in pl/sql that goes over numbers - sql

I want to write a loop that iterates over numbers 105 102 19 17 101 16 106 107
for each iteration I want to plug the number in a query and insert it into a table.
pseudo:
LOOP (105 102 19 17 101 16 106 107)
FETCH select * from some_table where value=current_iteration_index --105..etc.
INTO my_rec_type
END LOOP;

Another method:
declare
type numListType is table of number;
numList numListType;
begin
numList := numListType(
105,102,19,17,101,16,106,107
);
for i in numList.FIRST..numList.LAST loop
-- your usage of element goes here
dbms_output.put_line(numList(i));
end loop;
end;
/

Here's a more concise, albeit no less ugly, alternative:
DECLARE
CURSOR C IS
SELECT val
FROM (SELECT LEVEL val FROM dual CONNECT BY LEVEL < 1000)
WHERE val IN (105,102,19,17,101,16,106,107);
BEGIN
FOR R IN C LOOP
select *
INTO my_rec_type
from some_table
where value=R.val; --105..etc.
... more stuff
END LOOP;
END;
The advantage here (IMO) is you only need to modify the IN list and perhaps the limit on the CONNECT BY clause to change your results.

While there are a couple of solutions to your questions, but based on your handle I'm going to tell you that I think you're approaching this the wrong way - you're not taking advantage of the features of the database.
Can you explain why
select * from some_table where value in (105, 102, 19, 17, 101, 16, 106, 107)
doesn't do what you want it to do?

Here's an option, using a Cursor FOR LOOP, and the %ROWTYPE attribute:
DECLARE
my_rec_type SOME_TABLE%ROWTYPE;
CURSOR c IS
SELECT 105 AS c_index FROM DUAL
UNION ALL
SELECT 102 AS c_index FROM DUAL
UNION ALL
SELECT 19 AS c_index FROM DUAL
UNION ALL
SELECT 17 AS c_index FROM DUAL
UNION ALL
SELECT 101 AS c_index FROM DUAL
UNION ALL
SELECT 16 AS c_index FROM DUAL
UNION ALL
SELECT 106 AS c_index FROM DUAL
UNION ALL
SELECT 107 AS c_index FROM DUAL
BEGIN
FOR cursor_rec IN c
LOOP
SELECT *
INTO my_rec_type
FROM some_table
WHERE value = cursor_rec.c_index;
END LOOP;
END;

Related

I want to Create a function that returns the number of employees in a department

The query I tried is this,
CREATE OR REPLACE FUNCTION get_dno (d_no IN Employee.DepartmentNo%TYPE)
RETURN NUMBER
IS
d_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO d_cnt
FROM employee GROUP BY DeptNo=d_no;
RETURN d_cnt;
END get_dno;
and use this query below to get value from user.
DECLARE
get_dptno NUMBER;
BEGIN
get_dptno := get_dno(:d_no);
DBMS_OUTPUT.PUT_LINE('No of employees in searched DeptNo : ' ||get_dptno);
END;
But I couldn't even get the Function part to run.
The error I got was where I used GROUP BY, I think
This is the error message I got
Is there any workaround for the same logic ???
When you group by column there is no condition accept please remove code after column name DeptNo.
CREATE OR REPLACE FUNCTION get_dno (d_no IN Employee.DepartmentNo%TYPE)
RETURN NUMBER
IS
d_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO d_cnt
FROM employee GROUP BY DeptNo;
RETURN d_cnt;
END get_dno;
You are almost there, you want WHERE rather than GROUP BY and you need to work out if the column name is DepartmentNo or DeptNo and use the correct column name in the signature and in the SELECT query:
CREATE FUNCTION get_dno (
d_no IN Employee.DepartmentNo%TYPE
) RETURN NUMBER
IS
d_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO d_cnt
FROM employee
WHERE DepartmentNo=d_no;
RETURN d_cnt;
END get_dno;
/
Then:
DECLARE
get_dptno NUMBER;
BEGIN
get_dptno := get_dno(:d_no);
DBMS_OUTPUT.PUT_LINE('No of employees in searched DeptNo : ' ||get_dptno);
END;
/
Works and, for the sample data:
CREATE TABLE employee (id, departmentno) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 1 FROM DUAL UNION ALL
SELECT 5, 2 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 3 FROM DUAL UNION ALL
SELECT 8, 3 FROM DUAL UNION ALL
SELECT 9, 3 FROM DUAL UNION ALL
SELECT 10, 4 FROM DUAL;
If :dno is 1 then outputs:
No of employees in searched DeptNo : 4
db<>fiddle here

Error: inserting data into hana from oracle using dblink

I am trying to insert into hana database from oracle using dblink.I am getting the below error:
ERROR at line 1: ORA-20001: ORA-28500: connection from ORACLE to a
non-Oracle system returned this message: [SAP AG][LIBODBCHDB
SO][HDBODBC] General error;12000 user-defined error: line 2 col 142
(at pos 277): [12000] (range 3) user-defined error exception: no table
change allowed {HY000,NativeErr = 12000} ORA-06512: at
"POP_REMOTE_TABLES", line 58 ORA-06512: at line 1
Below is the code:
CREATE OR REPLACE VIEW VIEW_TEST_DYNAMIC
(
TRANS_OUT
)
AS
select 'insert into test_dynamic#dblink_hana(cust_id,address) values ('||cust_id||','''||address||''')' as trans_out from
(
select 10 cust_id,'9 Help Street, Level 4' address from dual union all
select 11 cust_id,'22 Victoria Street' address from dual union all
select 12 cust_id,'1495 Franklin Str.' address from dual union all
select 13 cust_id,'30 Hasivim St.,Petah-Tikva' address from dual union all
select 14 cust_id,'2 Jakaranda St' address from dual union all
select 15 cust_id,'61, Science Park Rd' address from dual union all
select 16 cust_id,'61, Social park road.' address from dual union all
select 17 cust_id,'Av. Hermanos Escobar 5756' address from dual union all
select 18 cust_id,'Ave. Hermanos Escobar 5756' address from dual union all
select 19 cust_id,'8000 W FLORISSANT Ave.' address from dual union all
select 20 cust_id,'8600 MEMORIAL PKWY SW' address from dual union all
select 21 cust_id,'8200 FLORISSANTMEMORIALWAYABOVE SW' address from dual union all
select 22 cust_id,'8600 MEMORIALFLORISSANT PKWY SW.' address from dual
) t1;
create table test_remote(local_table_name varchar2(50));
insert into test_remote values('VIEW_TEST_DYNAMIC');
procedure:
CREATE OR REPLACE PROCEDURE pop_remote_tables(p_table_name in varchar2 default 'ALL') as
l_cursor sys_refcursor;
l_trans_out varchar2(32000) := NULL;
l_processed_cnt number := 0;
cursor tab_cur is
select upper(local_table_name) local_table_name from test_remote where active_yn='Y' and
upper(local_table_name) = nvl(decode(upper(p_table_name),'ALL',NULL,upper(p_table_name)),upper(local_table_name));
BEGIN
dbms_output.put_line('Started');
for tab_rec in tab_cur
loop
dbms_output.put_line('Started processing:'||tab_rec.local_table_name);
l_processed_cnt := 0;
open l_cursor for 'select trans_out from ' || dbms_assert.sql_object_name(tab_rec.local_table_name);
loop
fetch l_cursor into l_trans_out;
exit when l_cursor%notfound;
execute immediate l_trans_out;
l_processed_cnt := l_processed_cnt + 1;
end loop;
commit;
close l_cursor;
dbms_output.put_line( 'No of records Inserted:' || l_processed_cnt);
end loop;
dbms_output.put_line('Ended');
EXCEPTION
when others then
raise_application_error(-20001, sqlerrm);
END;
/
sho err;
Can someone please help how to resolve this issue?

i get ORA-01722: invalid number error in plsql

i have problem about ora 01722 i have string prols it is like "1501,1701,1901,2001,2501,2601" or "2321,1331,111231,35501" and i want control table x' column x.rol_id include prols or not but i get error in my code. here is part of my code. thanks.
function get_menu_detay_01(pmenu_kod varchar2,
proller varchar2) return sys_refcursor is
v_cr_1 sys_refcursor;
begin
open v_cr_1 for
select distinct mtd.sira_no,
mtd.seviye_1,
mtd.fa_icon_1,
mtd.seviye_2,
mtd.fa_icon_2,
mtd.seviye_3,
mtd.fa_icon_3,
ut.uygulama_tanim_id,
ut.aciklama,
ut.path,
ut.fa_icon,
ut.href,
ut.onclick
from uat.menu_tanim_d mtd
left join uat.uygulama_tanim ut
on ut.uygulama_tanim_id = mtd.uygulama_tanim_id
left join uat.uygulama_yetki uy
on mtd.uygulama_tanim_id = uy.uygulama_tanim_id
where mtd.menu_kod = pmenu_kod
and uy.rol_id in (select regexp_substr(tt.rol, '[^,]+', 1, level)
from (select proller rol
from dual t) tt
connect by regexp_substr(tt.rol, '[^,]+', 1, level) is not null)
order by mtd.sira_no;
return v_cr_1;
end;
If you say the strings you are splitting has separator , then you need to change the regular expression [^,]+ to to include , instead ; or the other way around
I just tried and its working,
WITH tst
AS
(SELECT 1501 rol_id FROM dual
UNION ALL
SELECT 1701 FROM dual
UNION ALL
SELECT 1901 FROM dual
UNION ALL
SELECT 2001 FROM dual
UNION ALL
SELECT 2501 FROM dual
UNION ALL
SELECT 2601 FROM dual
)
SELECT *
FROM tst x
WHERE x.rol_id in (select regexp_substr(tt.rol, '[^,]+', 1, LEVEL)
from (select '1701,1901,2001,2501,2601' rol
from dual t) tt
connect by regexp_substr(tt.rol, '[^,]+', 1, level) is not null);
EDIT: Test with function Please check how you call the function and pass the string in proper format or not
CREATE OR REPLACE FUNCTION get_menu_detay_01(proller VARCHAR2)
RETURN SYS_REFCURSOR
IS
v_cr_1 SYS_REFCURSOR;
BEGIN
OPEN v_cr_1 FOR
SELECT *
FROM ( SELECT 1501 rol_id FROM dual
UNION ALL
SELECT 1701 FROM dual
UNION ALL
SELECT 1901 FROM dual
UNION ALL
SELECT 2001 FROM dual
UNION ALL
SELECT 2501 FROM dual
UNION ALL
SELECT 2601 FROM dual
) x
WHERE x.rol_id IN (SELECT regexp_substr(tt.rol,'[^,)]+',1,LEVEL)
FROM (SELECT proller rol FROM dual t) tt
CONNECT BY regexp_substr(tt.rol,'[^,]+',1,LEVEL) IS NOT NULL);
RETURN v_cr_1;
END get_menu_detay_01;
Test:
set serveroutput on;
DECLARE
lo_ref_cur SYS_REFCURSOR;
lo_number NUMBER;
BEGIN
lo_ref_cur := get_menu_detay_01('1701,1901,2001,2501,2601');
LOOP
FETCH lo_ref_cur INTO lo_number;
EXIT WHEN lo_ref_cur%NOTFOUND;
dbms_output.put_line(lo_number);
END LOOP;
END;
/
Output:
------
1701
1901
2001
2501
2601
PL/SQL procedure successfully completed.

How can I pass comma separated value in cursor's select statement where clause

I have following Block which has a cursor and a select query. I want to pass the output of select, which is comma separated into
the cursor's select statement, into the where clause.
I know below code will throw an error because of SQL query in Declare section but how I achieve this using array or collection.
here, id column is number
code snippet:
declare
test varchar2(30);
SELECT LISTAGG(value, ', ') WITHIN GROUP (ORDER BY value2) into test from table3 where value2=12;
cursor c1 (select * from table where id in (test))
begin
for i in c1 loop
null;
end loop;
end;
Why would you ever do this?
You can simple write you select as:
Select * from table where id in (select value from table3 where value2=12)
Edit:
Also you need to open your cursor c1, for it to work AFAIK.
You can use a collection and the MEMBER OF operator:
Oracle Setup:
CREATE TYPE IntegerList AS TABLE OF NUMBER(8,0);
/
CREATE TABLE table1 ( value, value2 ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 3, 4 FROM DUAL UNION ALL
SELECT 5, 3 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL;
CREATE TABLE table2 ( id, value ) AS
SELECT 1, 11 FROM DUAL UNION ALL
SELECT 2, 22 FROM DUAL UNION ALL
SELECT 3, 33 FROM DUAL UNION ALL
SELECT 7, 77 FROM DUAL;
PL/SQL:
DECLARE
test IntegerList;
c1 SYS_REFCURSOR;
BEGIN
SELECT value
BULK COLLECT INTO test
FROM table1;
FOR r IN ( SELECT * FROM table2 WHERE id MEMBER OF test ) LOOP
DBMS_OUTPUT.PUT_LINE( r.id || ', ' || r.value );
END LOOP;
END;
/
Output:
1, 11
3, 33
7, 77
db<>fiddle here

Splitting comma separated values in Oracle

I have column in my database where the values are coming like the following:
3862,3654,3828
In dummy column any no. of comma separated values can come. I tried with following query but it is creating duplicate results.
select regexp_substr(dummy,'[^,]+',1,Level) as dummycol
from (select * from dummy_table)
connect by level <= length(REGEXP_REPLACE(dummy,'[^,]+'))+1
I am not understanding the problem. Can anyone can help?
Works perfectly for me -
SQL> WITH dummy_table AS(
2 SELECT '3862,3654,3828' dummy FROM dual
3 )
4 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
5 FROM dummy_table
6 CONNECT BY level <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
7 /
DUMMYCOL
--------------
3862
3654
3828
SQL>
There are many other ways of achieving it. Read Split single comma delimited string into rows.
Update Regarding the duplicates when the column is used instead of a single string value. Saw the use of DBMS_RANDOM in the PRIOR clause to get rid of the cyclic loop here
Try the following,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
7 FROM dummy_table
8 CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
9 AND prior rn = rn
10 AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
11 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
SQL>
Update 2
Another way,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678,xyz' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(t.dummy, '[^,]+', 1, levels.column_value)) AS dummycol
7 FROM dummy_table t,
8 TABLE(CAST(MULTISET
9 (SELECT LEVEL
10 FROM dual
11 CONNECT BY LEVEL <= LENGTH (regexp_replace(t.dummy, '[^,]+')) + 1
12 ) AS sys.OdciNumberList)) LEVELS
13 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
xyz
6 rows selected.
SQL>
Giving a PL/SQL example where parsing over a table with an ID and column name. This will parse and print out each ID and the parsed value which could then be inserted into a new table or used in some other way.
Input
Column_ID Column_Name
123 (3862,3654,3828)
Output
Column_ID Column_Name
123 3862
123 3654
123 3828
PL/SQL Code
declare
table_name1 varchar2(1000);
string_to_parse varchar2(2000); -- assign string to table name
string_length number := 0; -- string length for loop
string_value varchar2(2000); -- string value to store value in
column_id number;
begin
--some table in the format '123' as column_id, '(3862,3654,3828)' as column_name
--remove the parenthesis or other special characters if needed
update some_table t
set t.column_name = regexp_replace(t.column_name,'\(|\)','');
commit;
for i in (
select * from some_table
) loop
column_id := i.column_id; --assign the id of the colors
string_to_parse := i.column_name; -- assign string to be parsed
if string_to_parse is null then
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
else
--String to parse is the comma
string_to_parse := string_to_parse||',';
string_length := length(string_to_parse) - length(replace(string_to_parse,',',''));
-- Loop through string from parameter
for i in 1 .. string_length loop
-- [^,] matches any character except for the ,
select regexp_substr(string_to_parse,'[^,]+',1,i)
into string_value -- stores value into string_value
from dual; -- dual is a dummy table to work around
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
--clear out the string value
string_value := null;
end loop;
end if;
end loop;
end;