How to print random from table in a procedure oracle? - oracle

Let's say I have a table child and I want in a procedure to select random from 1 to 5, random values from the table and print them. How can i do it?
create table child(name varchar2(20), age number);
insert into child(name, age) values('A',5);
insert into child(name, age) values('B',12);
insert into child(name, age) values('C',7);
insert into child(name, age) values('D',4);
create or replace procedure random_child
as
l_name child.name%type;
l_age child.age%type;
begin
for i in(select dbms_random.value(1,5)
from (select name from child sample(50)))
loop
DBMS_OUTPUT.put_line(i.name);
end loop;
end;
It gives me a PLS-00302: name must be declared

You can use:
CREATE PROCEDURE random_child
AS
BEGIN
FOR i in(
SELECT name,
FLOOR(DBMS_RANDOM.VALUE(1,6)) AS value
FROM child
SAMPLE(50)
)
LOOP
DBMS_OUTPUT.put_line(i.name || ' ' || i.value);
END LOOP;
END;
/
Then:
BEGIN
random_child();
END;
/
May randomly output:
B 3
C 2
D 2
db<>fiddle here

There's no i.name there; alias is missing at the end of line #6:
SQL> create or replace procedure random_child
2 as
3 l_name child.name%type;
4 l_age child.age%type;
5 begin
6 for i in(select dbms_random.value(1,5) as name --> here
7 from (select name from child sample(50)))
8 loop
9 DBMS_OUTPUT.put_line(i.name);
10 end loop;
11 end;
12 /
Procedure created.
SQL> exec random_child
1,30966411991963041689918865935551009464
1,13993832387089615287177388489291237644
3,85292920191145794430114472793297022632
PL/SQL procedure successfully completed.
SQL>

Related

How to pass string of comma-separated numbers to stored procedure in condition for numeric field?

I have a stored procedure like below where multiple employee IDs will be passed as comma-separated value (multiple IDs). It is throwing error as "ORA-01722: invalid number". I know it's because of passing varchar2 variable for the numeric ID column. But is there any way we can achieve this simply?
create or replace PROCEDURE Fetch_Emp_Name(Emp_id in varchar2)
IS
BEGIN
select Name from EMP where id in (emp_id);
END;
You can use dynamic sql.
create or replace PROCEDURE Fetch_Emp_Name(emp_id in varchar2) IS
v_result varchar2;
begin
execute immediate
'select Name from EMP where id in (' || 'emp_id' || ')'
into
v_result;
end;
Also you can use package dbms_sql for dynamic sql.
Update
Another approach. I think may be better.
create or replace PROCEDURE Fetch_Emp_Name(emp_id in varchar2) IS
v_result varchar2;
begin
select
Name
from
EMP
where
id in
(
select
to_number(regexp_substr(emp_id, '[^,]+', 1, level))
from
dual
connect by regexp_substr(emp_id, '[^,]+', 1, level) is not null
);
exception
when no_data_found then
-- error1;
when too_many_rows then
-- error2;
end;
Sorry for before, I did not get the question in the right way. If you get a lot of IDs as different parameters, you could retrieve the list of names as an string split by comma as well. I put this code where I handled by regexp_substr the name of different emp_ids you might enter in the input parameter.
Example ( I am assuming that the IDs are split by comma )
create or replace PROCEDURE Fetch_Emp_Name(p_empid in varchar2) IS
v_result varchar2(4000);
v_append emp.name%type;
v_emp emp.emp_id%type;
counter pls_integer;
i pls_integer;
begin
-- loop over the ids
counter := REGEXP_COUNT(p_empid ,'[,]') ;
--dbms_output.put_line(p_empid);
if counter > 0
then
i := 0;
for r in ( SELECT to_number(regexp_substr(p_empid,'[^,]+',1,level)) as mycol FROM dual CONNECT BY LEVEL <= REGEXP_COUNT(p_empid ,'[,]')+1 )
loop
--dbms_output.put_line(r.mycol);
v_emp := r.mycol ;
select name into v_append from emp where emp_id = v_emp;
if i < 1
then
v_result := v_append ;
else
v_result := v_result ||','|| v_append ;
end if;
i := i + 1;
end loop;
else
v_emp := to_number(p_empid);
select name into v_result from emp where emp_id = v_emp;
end if;
dbms_output.put_line(v_result);
exception
when no_data_found
then
raise_application_error(-20001,'Not Employee found for '||v_emp||' ');
when too_many_rows
then
raise_application_error(-20002,'Too many employees for id '||v_emp||' ');
end;
Test
SQL> create table emp ( emp_id number, name varchar2(2) ) ;
Table created.
SQL> insert into emp values ( 1 , 'AA' );
1 row created.
SQL> insert into emp values ( 2 , 'BB' ) ;
1 row created.
SQL> commit;
SQL> insert into emp values ( 3 , 'CC' ) ;
1 row created.
SQL> select * from emp ;
EMP_ID NA
---------- --
1 AA
2 BB
3 CC
SQL> exec Fetch_Emp_Name('1') ;
AA
PL/SQL procedure successfully completed.
SQL> exec Fetch_Emp_Name('1,2,3') ;
AA,BB,CC
PL/SQL procedure successfully completed.
SQL>

fetching table data into a table using cursor

I have a table called phonebook and it has two columns (firstName, LastName). I want to create a table of lastName index by firstName using cursor, and I wrote this code:
CREATE OR REPLACE PROCEDURE proc1 AS
TYPE tableNames IS TABLE OF VARCHAR2(20) INDEX BY VARCHAR(20);
v1 tableNames;
v_firstName PHONEBOOK.FIRSTNAME%TYPE;
v_lastName PHONEBOOK.LASTNAME%TYPE;
CURSOR c_name IS SELECT FIRSTNAME, LASTNAME FROM PHONEBOOK;
BEGIN
OPEN c_name;
LOOP
FETCH c_name INTO v_firstName, v_lastName;
EXIT WHEN c_name%NOTFOUND;
v1(v_firstName) := v_lastName;
END LOOP;
FOR idx IN v1.FIRST..v1.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (v1(idx));
END LOOP;
CLOSE c_name;
END;
/
It has been successfully compiled. When I run this procedure it should print lastNames which stored in the tableNames but it gave me an error:
ORA-06502 "PL/SQL: numeric or value error"
Cause: An arithmetic, numeric, string, conversion, or constraint error
occurred. For example, this error occurs if an attempt is made to
assign the value NULL to a variable declared NOT NULL, or if an
attempt is made to assign an integer larger than 99 to a variable
declared NUMBER(2).
Action: Change the data, how it is manipulated, or how it is declared so
that values do not violate constraints.
Please help me to solve this problem
Not FOR, but WHILE. Also, I used cursor FOR loop as a source; easier to write & maintain.
SQL> create table phonebook (firstname varchar2(10), lastname varchar2(10));
Table created.
SQL> insert into phonebook
2 select 'Little', 'Foot' from dual union all
3 select 'Mc' , 'Donalds' from dual;
2 rows created.
SQL> create or replace procedure proc1 as
2 type tablenames is table of varchar2(10) index by varchar2(10);
3 v1 tablenames;
4 idx varchar2(10);
5 begin
6 for cur_r in (select firstname, lastname
7 from phonebook
8 )
9 loop
10 v1(cur_r.firstname) := cur_r.lastname;
11 end loop;
12
13 idx := v1.first;
14 while idx is not null loop
15 dbms_output.put_line(v1(idx));
16 idx := v1.next(idx);
17 end loop;
18 end;
19 /
Procedure created.
SQL> exec proc1;
Foot
Donalds
PL/SQL procedure successfully completed.
SQL>

PL/SQL loop table columes to variable

I have a table with two columes: product (varchar) and usage (varchar). I need to loop the table to one variable in the format "product;usage;product;usage...". How can this be done? The following loop does not work:
FOR product IN (SELECT product FROM licence_check) LOOP
product_usage := (licence_check.product || ';' || licence_check.usage);
END LOOP;
Does it have to be a loop? For not-that-many values, LISTAGG might do the job:
SQL> select listagg(dname||';'||loc, ';') within group (order by deptno) result
2 from dept;
RESULT
--------------------------------------------------------------------------------
ACCOUNTING;NEW YORK;RESEARCH;DALLAS;SALES;CHICAGO;OPERATIONS;BOSTON
SQL>
If it has to be a LOOP, I'd say that you miss concatenation with the PRODUCT_USAGE itself. Have a look:
SQL> declare
2 l_result varchar2(100);
3 begin
4 for cur_r in (select dname, loc from dept) loop
5 l_result := l_result || cur_r.dname ||';'||cur_r.loc ||';';
-----------
This is what you miss
6 end loop;
7 dbms_output.put_line(rtrim(l_result, ';'));
8 end;
9 /
ACCOUNTING;NEW YORK;RESEARCH;DALLAS;SALES;CHICAGO;OPERATIONS;BOSTON
PL/SQL procedure successfully completed.
SQL>

How to get n element from nested table pl/sql?

I have problem with the compilation of my stored procedure.
create or replace type CartLine as object (
offeringId OfferingIdList
,productLine varchar2(50)
,equipment char(1)
,installment CHAR(1)
,cartItemProcess varchar2(50)
,minimalPrice decimal
);
create or replace type CartLineType is table of CartLine;
create or replace PROCEDURE GetOfferingRecommendation (
cartLineList IN CartLineType,
user IN UserType,
customer IN CustomerType,
processContext IN ProcessContextType,
recommendation out SYS_REFCURSOR )
IS
prodLine VARCHAR2(20);
prodPrice NUMBER(5,0);
BEGIN
FOR i IN cartLineList.FIRST .. cartLineList.LAST
LOOP
SELECT productLine, minimalPrice
INTO prodLine, prodPrice
FROM TABLE(cartLineList(i));
OPEN recommendation FOR
SELECT CAST(REKOM_ID_SEQ.NEXTVAL AS VARCHAR(10))
||'_'||cp.ID_REKOM_OFERTA
||'_'||TO_CHAR(SYSDATE, 'yyyymmdd') AS recommendationId
,cp.ID_REKOM_OFERTA AS offeringId
,cp.PRIORYTET AS priority
FROM REKOM_CROSS_PROM cp
WHERE cp.LINIA_PROD = prodLine
AND prodPrice BETWEEN cp.CENA_MIN AND cp.CENA_MAX
;
END LOOP;
END GetOfferingRecommendation;
It is not getting compiled cause the following statement is wrong:
SELECT productLine, minimalPrice
INTO prodLine, prodPrice
FROM TABLE(cartLineList(i));
I want to select only single value every all new iteration of my loop.
Can somebody help me to resolve my problem?
-- EDIT 1/9/2018 4:26 PM
According to topic:
How to return result of many select statements as one custom table
I tried to rebuild my procedure.
I created types for test:
create or replace TYPE tst AS OBJECT (
rekom_id varchar2(50)
,rekom_priorytet number(5,4)
);
/
create or replace TYPE tst_list IS TABLE OF tst;
After that, I changed my procedure like below:
CREATE OR REPLACE PROCEDURE GetOfferingRecommendation (cartLineList IN CartLineType, recommendation out SYS_REFCURSOR )
IS
CURSOR CUR_TAB IS SELECT productLine, minimalPrice FROM TABLE(cartLineList);
v_tst tst_list;
BEGIN
FOR i IN CUR_TAB
LOOP
EXECUTE IMMEDIATE 'SELECT tst_list(
CAST(REKOM_ID_SEQ.NEXTVAL AS VARCHAR(10))||''_''||cp.ID_REKOM_OFERTA||''_''||TO_CHAR(SYSDATE, ''yyyymmdd'')
,cp.PRIORYTET)
FROM REKOM_CROSS_PROM cp
WHERE cp.LINIA_PROD ='||i.productLine||' AND '||i.minimalPrice||' BETWEEN cp.CENA_MIN AND cp.CENA_MAX'
BULK COLLECT INTO v_tst;
EXIT WHEN CUR_TAB%NOTFOUND;
FOR REC IN 1 .. v_tst.COUNT
LOOP
PIPE ROW (v_tst(REC));
END LOOP;
END LOOP;
OPEN recommendation FOR SELECT * FROM TABLE(v_tst);
END IF;
END GetOfferingRecommendation;
But I can't compile because error occured: PLS-00629
Would you please told me what I do wrong?
You cannot assign variables using a select statement from a collection in a loop like below.
SELECT productLine, minimalPrice
INTO prodLine, prodPrice
FROM TABLE(cartLineList(i));
The collection elements cannot be referred inside a SELECT statement 1 by 1 using a loop. You can loop through the collection as
For i in 1..collection.count
loop
...
..
End loop;
Collection has a number of rows and when you do so, you try to assign many rows to a single variable, which is wrong. You can do either of the below explained. There relevant explanation is inline.
CREATE OR REPLACE PROCEDURE GETOFFERINGRECOMMENDATION (
CARTLINELIST IN CARTLINETYPE,
RECOMMENDATION OUT SYS_REFCURSOR)
IS
TYPE V_PRODLINE IS TABLE OF VARCHAR2 (20)
INDEX BY PLS_INTEGER;
TYPE V_PRODPRICE IS TABLE OF NUMBER (5, 0)
INDEX BY PLS_INTEGER;
PRODLINE V_PRODLINE;
PRODPRICE V_PRODPRICE;
BEGIN
--Putting the collection result to another collection
SELECT PRODUCTLINE,
MINIMALPRICE
BULK COLLECT INTO PRODLINE,
PRODPRICE
FROM TABLE (CARTLINELIST);
-- Assuming number of elements will be same in both prodLine, prodPrice colection, loop can be iterated as below
FOR I IN 1 .. PRODLINE.LAST
LOOP
OPEN RECOMMENDATION FOR
SELECT CAST (REKOM_ID_SEQ.NEXTVAL AS VARCHAR (10) )
|| '_'
|| CP.ID_REKOM_OFERTA
|| '_'
|| TO_CHAR (SYSDATE, 'yyyymmdd') AS RECOMMENDATIONID,
CP.ID_REKOM_OFERTA AS OFFERINGID,
CP.PRIORYTET AS PRIORITY
FROM REKOM_CROSS_PROM CP
WHERE CP.LINIA_PROD = PRODLINE (I)
AND PRODPRICE (I) BETWEEN CP.CENA_MIN AND CP.CENA_MAX;
END LOOP;
END GETOFFERINGRECOMMENDATION;
OR as per #krokodilko.. You can do as below:
CREATE OR REPLACE PROCEDURE GETOFFERINGRECOMMENDATION (
CARTLINELIST IN CARTLINETYPE,
RECOMMENDATION OUT SYS_REFCURSOR)
IS
PRODLINE VARCHAR2 (20);
PRODPRICE NUMBER (5, 0);
BEGIN
FOR I IN 1 .. CARTLINELIST.LAST
LOOP
--Assign the values of the collection to the variable declared.
PRODUCTLINE := CARTLINELIST (I).PRODUCTLINE;
MINIMALPRICE := CARTLINELIST (I).MINIMALPRICE;
OPEN RECOMMENDATION FOR
SELECT CAST (REKOM_ID_SEQ.NEXTVAL AS VARCHAR (10) )
|| '_'
|| CP.ID_REKOM_OFERTA
|| '_'
|| TO_CHAR (SYSDATE, 'yyyymmdd') AS RECOMMENDATIONID,
CP.ID_REKOM_OFERTA AS OFFERINGID,
CP.PRIORYTET AS PRIORITY
FROM REKOM_CROSS_PROM CP
WHERE CP.LINIA_PROD = PRODLINE
AND PRODPRICE BETWEEN CP.CENA_MIN AND CP.CENA_MAX;
END LOOP;
END GETOFFERINGRECOMMENDATION;
Demo:
SQL> CREATE OR REPLACE TYPE CARTLINE AS OBJECT (
2 PRODUCTLINE VARCHAR2 (50),
3 MINIMALPRICE DECIMAL
4 );
5 /
Type created.
SQL> CREATE OR REPLACE TYPE CARTLINETYPE IS TABLE OF CARTLINE;
2 /
Type created.
SQL> CREATE OR REPLACE PROCEDURE GETOFFERINGRECOMMENDATION (
2 CARTLINELIST IN CARTLINETYPE)
3 IS
4 TYPE V_PRODLINE IS TABLE OF VARCHAR2 (20)
5 INDEX BY PLS_INTEGER;
6
7 TYPE V_PRODPRICE IS TABLE OF NUMBER (5, 0)
8 INDEX BY PLS_INTEGER;
9
10 PRODLINE V_PRODLINE;
11 PRODPRICE V_PRODPRICE;
12 BEGIN
13 SELECT PRODUCTLINE,
14 MINIMALPRICE
15 BULK COLLECT INTO PRODLINE,
16 PRODPRICE
17 FROM TABLE (CARTLINELIST);
18
19 FOR I IN 1 .. PRODLINE.COUNT
20 LOOP
21 DBMS_OUTPUT.PUT_LINE ( 'Prod Line '
22 || PRODLINE (I)
23 || ' Prod Price '
24 || PRODPRICE (I) );
25 END LOOP;
26 END GETOFFERINGRECOMMENDATION;
27 /
Procedure created.
Output:
SQL> DECLARE
2 VAR CARTLINETYPE := CARTLINETYPE ();
3 BEGIN
4 --Popuating the collection
5 VAR.EXTEND (2);
6 VAR (1) := CARTLINE ('TypeA', 6.0);
7 VAR (2) := CARTLINE ('TypeB', 7.1);
8
9 --Calling the procedure
10 GETOFFERINGRECOMMENDATION (CARTLINELIST => VAR);
11 END;
12 /
Prod Line TypeA Prod Price 6
Prod Line TypeB Prod Price 7
PL/SQL procedure successfully completed.
SQL>
Use simple assignments instead of SELECT ... FROM TABLE(cartLineList(i));:
LOOP
/* SELECT productLine, minimalPrice INTO prodLine, prodPrice FROM TABLE(cartLineList(i)); */
productLine := cartLineList(i).productLine;
minimalPrice := cartLineList(i).minimalPrice;
.....
.....
END LOOP;

using cursor attributes in a CURSOR FOR LOOP

I am running the following in the Scott schema:
SET serveroutput ON;
BEGIN
FOR c_Emp IN (SELECT * FROM emp)
LOOP
dbms_output.put_line('The record processed by the cursor ' || c_Emp%rowcount);
END LOOP;
end;
This gives the error:
cursor attribute may not be applied to non-cursor 'C_EMP'
However if this is done using an explicit cursor it works fine:
set serveroutput on ;
DECLARE
emp_record emp%ROWTYPE;
count_variable NUMBER;
CURSOR c IS
SELECT * FROM emp;
BEGIN
OPEN c;
loop
fetch c INTO emp_record;
exit WHEN c%notfound;
dbms_output.put_line ('The record count is ' || c%rowcount);
END loop;
close c;
end;
Just want to understand : whether while using the CURSOR FOR LOOP, is the index variable not a cursor attribute, if so why? could someone plz expalin this....
c_Emp is not the cursor, its a record with felds for each column in the SELECT statment
c_Emp is similar to the emp_record from your second example.
Even while using a FOR loop the cursor has to be explicitly defined.
A sample use of FOR loop with a cursor would look like below:
declare
cursor c1 is select a from table;
begin
FOR b in c1
loop
<required logic>
end loop;
end;
To get the index in a for loop, you can add the rownum pseudocolumn in the select clause of implicit cursor.
SET serveroutput ON;
BEGIN
FOR c_Emp IN (SELECT e.*, rownum FROM emp e)
LOOP
dbms_output.put_line('The record processed by the cursor ' || c_Emp.rownum);
END LOOP;
end;
Try this:
SET serveroutput ON;
DECLARE
x NUMBER :=0 ;
BEGIN
FOR c_Emp IN (SELECT * FROM emp)
LOOP
x := x+1;
dbms_output.put_line('The record processed by the cursor ' || x);
END LOOP;
-----
IF x>0 THEN
dbms_output.put_line('Cursr was opened');
ELSE
dbms_output.put_line('Cursr was not opened');
END IF;
end;
SQL> create table product(
2 product_id number(4) not null,
3 product_description varchar2(20) not null
4 );
Table created.
SQL>
SQL> insert into product values (1,'Java');
1 row created.
SQL> insert into product values (2,'Oracle');
1 row created.
SQL> insert into product values (3,'C#');
1 row created.
SQL> insert into product values (4,'Javascript');
1 row created.
SQL> insert into product values (5,'Python');
1 row created.
SQL> create table company(
2 product_id number(4) not null,
3 company_id NUMBER(8) not null,
4 company_short_name varchar2(30) not null,
5 company_long_name varchar2(60)
6 );
Table created.
SQL> insert into company values(1,1001,'A Inc.','Long Name A Inc.');
1 row created.
SQL> insert into company values(1,1002,'B Inc.','Long Name B Inc.');
1 row created.
SQL> insert into company values(1,1003,'C Inc.','Long Name C Inc.');
1 row created.
SQL> insert into company values(2,1004,'D Inc.','Long Name D Inc.');
1 row created.
SQL> insert into company values(2,1005,'E Inc.','Long Name E Inc.');
1 row created.
SQL> insert into company values(2,1006,'F Inc.','Long Name F Inc.');
1 row created.
SQL> DECLARE
2 CURSOR cursorValue IS
3 SELECT h.product_description,o.company_short_name FROM company o,product h
4 WHERE o.product_id =h.product_id
5 ORDER by 2;
6 num_total_rows NUMBER;
7 BEGIN
8
9 FOR idx IN cursorValue LOOP
10 dbms_output.put_line(rpad(idx.product_description,20,' ')||' '||
11 rpad(idx.company_short_name,30,' '));
12
13 num_total_rows :=cursorValue%ROWCOUNT;
14 END LOOP;
15 IF num_total_rows >0 THEN
16 dbms_output.new_line;
17 dbms_output.put_line('Total Organizations = '||to_char(num_total_rows));
18 END IF;
19 END;
20 /
Java A Inc.
Java B Inc.
Java C Inc.
Oracle D Inc.
Oracle E Inc.
Oracle F Inc.
Total Organizations = 6
PL/SQL procedure successfully completed.
SQL> drop table product;
Table dropped.
SQL> drop table company;
Table dropped.
enter code here

Resources