Update Column using pl/sql - oracle

I have a table T1 which 3 columns with 100 records. All columns and rows were filled but the first column named ID values are empty. So, I wanted to fill the ID column with numbering order(1,2..100) for 100 rows by using PL/SQL Program. I have tried with rownum and with a sequence which is working fine. I want to try with pl/SQL block. I have also tried to write pl/SQL block, however, not getting the desired result.
declare
count1 number;
begin
SELECT COUNT(1) INTO COUNT1 FROM T1;
FOR I IN 1..COUNT1
loop
UPDATE T1 SET ID =I;
end loop;
end;

SQL should be the way to do it; but OK, if you're learning PL/SQL, then this might be one option:
Sample table (ID column should be populated):
SQL> create table test (id number, name varchar2(10));
Table created.
SQL> insert into test (name) select ename from emp;
14 rows created.
SQL> select * From test;
ID NAME
---------- ----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER
14 rows selected.
Anonymous PL/SQL block:
SQL> declare
2 i number := 1;
3 begin
4 for cur_r in (select rowid rid from test) loop
5 update test set id = i where rowid = cur_r.rid;
6 i := i + 1;
7 end loop;
8 end;
9 /
PL/SQL procedure successfully completed.
Result:
SQL> select * From test;
ID NAME
---------- ----------
1 SMITH
2 ALLEN
3 WARD
4 JONES
5 MARTIN
6 BLAKE
7 CLARK
8 SCOTT
9 KING
10 TURNER
11 ADAMS
12 JAMES
13 FORD
14 MILLER
14 rows selected.
SQL>
Loops are slow, they process the table row-by-row. Yet another option (you didn't mention and - perhaps - didn't try - is merge.
SQL> update test set id = null;
14 rows updated.
SQL> begin
2 merge into test a
3 using (select b.rowid,
4 row_number() over (order by null) rn
5 from test b
6 ) x
7 on (a.rowid = x.rowid)
8 when matched then update set
9 a.id = x.rn;
10 end;
11 /
PL/SQL procedure successfully completed.
SQL> select * from test;
ID NAME
---------- ----------
1 SMITH
2 ALLEN
3 WARD
4 JONES
5 MARTIN
6 BLAKE
7 CLARK
8 SCOTT
9 KING
10 TURNER
11 ADAMS
12 JAMES
13 FORD
14 MILLER
14 rows selected.
SQL>

Related

Write a function to find out the hire date of an employee who has joined just prior to the given date passed as input parameter in plsql

how to use ampersand in this program
create or replace function p_hire_date return date is
&v_hire_date employees.hire_date%type;
begin
select hire_date into v_hire_date
from employees
where hire_date < v_hire_date;
return v_hire_date;
end;
error PLS-00103:
Why would you use a substitution variable? This is a function, pass parameter to it and use it in its code.
Example is based on Scott's sample schema; adjust it to your own.
SQL> CREATE OR REPLACE FUNCTION f_hire_date (par_date IN DATE)
2 RETURN DATE
3 IS
4 retval DATE;
5 BEGIN
6 SELECT MAX (a.hiredate)
7 INTO retval
8 FROM emp a
9 WHERE a.hiredate < par_date;
10
11 RETURN retval;
12 END;
13 /
Function created.
SQL>
(You don't have to do that; it's just to know what dates represent):
SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'dd.mm.yyyy';
Session altered.
Sample data:
SQL> SELECT ename, hiredate
2 FROM emp
3 ORDER BY hiredate;
ENAME HIREDATE
---------- ----------
SMITH 17.12.1980
ALLEN 20.02.1981
WARD 22.02.1981
JONES 02.04.1981
BLAKE 01.05.1981
CLARK 09.06.1981
TURNER 08.09.1981
MARTIN 28.09.1981 --> If I pass 30.09.1981, I'll get this date
KING 17.11.1981
JAMES 03.12.1981
FORD 03.12.1981
MILLER 23.01.1982
SCOTT 09.12.1982
ADAMS 12.01.1983
14 rows selected.
Let's try it:
SQL> SELECT f_hire_date (DATE '1981-09-30') FROM DUAL;
F_HIRE_DAT
----------
28.09.1981
SQL>

Not able to convert this SQL query to PL/SQL

The question is to retrieve all the students name who have scored more than average marks. So, As i am familiar in SQL i have came up with this query:
SELECT Student_Name
FROM Student
WHERE Mark >
( SELECT AVG(Mark)
FROM Student
);
But i need this query to be in PL/SQL format. Please help me.
It depends on what you want to do, once you convert it to PL/SQL. In PL/SQL, you have to fetch the result into something. For example (based on Scott's schema):
SQL> select ename
2 from emp
3 where sal > (select avg(sal) from emp);
ENAME
----------
JONES
BLAKE
CLARK
SCOTT
KING
FORD
6 rows selected.
You could select into an array:
SQL> set serveroutput on
SQL> declare
2 l_tab sys.odcivarchar2list;
3 begin
4 select ename
5 bulk collect into l_tab
6 from emp
7 where sal > (select avg(sal) from emp);
8
9 for i in l_tab.first .. l_tab.last loop
10 dbms_output.put_line(l_tab(i));
11 end loop;
12 end;
13 /
JONES
BLAKE
CLARK
SCOTT
KING
FORD
PL/SQL procedure successfully completed.
SQL>
Or, you can process those rows directly in a loop:
SQL> begin
2 for cur_e in (select ename
3 from emp
4 where sal > (select avg(sal) from emp))
5 loop
6 dbms_output.put_line(cur_e.ename);
7 end loop;
8 end;
9 /
JONES
BLAKE
CLARK
SCOTT
KING
FORD
PL/SQL procedure successfully completed.
SQL>
Or, you can make a function out of it and return refcursor:
SQL> create or replace function f_test
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 open rc for select ename
7 from emp
8 where sal > (select avg(sal) from emp);
9 return rc;
10 end;
11 /
Function created.
SQL> select f_test from dual;
F_TEST
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ENAME
----------
JONES
BLAKE
CLARK
SCOTT
KING
FORD
6 rows selected.
SQL>
There might be other options as well. Basically, it just depends on the requirement.

Insert into table collection type in table function without using explicit cursor in PL/SQL

I write the following code in PL/SQL and it works:
declare
type deliveryStat_o IS record (
warehouseName varchar2(20), shipMode char(30), thirty_days number, sixty_days number,
ninety_days number, oneTwenty_days number, veryLate number
);
type deliveryStat_t is TABLE OF deliveryStat_o;
statTable deliveryStat_t;
begin
SELECT w_warehouse_name, sm_type, 1 AS thirty_days, 1 AS sixty_days, 1 AS ninety_days,
1 AS oneTwenty_days, 1 AS veryLateDelivery
bulk collect into statTable
FROM catalog_sales, warehouse, ship_mode, date_dim
WHERE cs_ship_date_sk = d_date_sk
AND cs_warehouse_sk = w_warehouse_sk
AND cs_ship_mode_sk = sm_ship_mode_sk
GROUP BY w_warehouse_name,
sm_type ;
end;
How can I do this inside a table function that returns the nested collection statTable. I understand that this can probably be accomplished using explicit cursors; however, is it possible to do it without using a cursor?
For context, I'm starting with this as the base
SQL> set serverout on
SQL> declare
2 type deliveryStat_o IS record (
3 empno number, ename varchar2(20)
4 );
5 type deliveryStat_t is TABLE OF deliveryStat_o;
6
7 statTable deliveryStat_t;
8
9 begin
10
11 SELECT empno, ename
12 bulk collect into statTable
13 FROM emp;
14 dbms_output.put_line('recs='||statTable.count);
15 end;
16 /
recs=14
PL/SQL procedure successfully completed.
To convert that to allow a table function, then those types need to be SQL types, hence
SQL> create or replace
2 type deliveryStat_o as object (
3 empno number, ename varchar2(20)
4 );
5 /
Type created.
SQL> create or replace
2 type deliveryStat_t as table of deliveryStat_o
3 /
Type created.
Now that this is done, the query must return a table of objects, so
SQL> set serverout on
SQL> declare
2 statTable deliveryStat_t;
3 begin
4
5 SELECT deliveryStat_o(empno, ename)
6 bulk collect into statTable
7 FROM emp;
8 dbms_output.put_line('recs='||statTable.count);
9 end;
10 /
recs=14
PL/SQL procedure successfully completed.
which can now be easily converted to a table function
SQL> create or replace
2 function my_func return deliveryStat_t is
3 statTable deliveryStat_t;
4 begin
5
6 SELECT deliveryStat_o(empno, ename)
7 bulk collect into statTable
8 FROM emp;
9 return statTable;
10 end;
11 /
Function created.
SQL> select * from my_func();
EMPNO ENAME
---------- --------------------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
If you're returning a LOT of rows, then consider a pipelined function instead to avoid the memory overhead of collecting all the rows into the nested table

How to pass multiple parameters in oracle stored procedure

i am trying to execute like exec print_emp(1010,1111) but it will showing error.
create or replace
procedure print_emp(
P_empno NUMBER
)
IS
begin
for c in ( SELECT *
from emp
where empno in p_empno)
loop
dbms_output.put_line( c.empno||' '||c.ename||' '||c.job||' '||c.sal);
end loop;
END;
You'll need to create a type that is a collection of numbers, and then a procedure that accepts that collection.
Rather than use the IN operator, you should use MEMBER OF to test whether a scalar value is in a collection.
create or replace type tab_number is table of number
/
create or replace procedure print_nums
(p_nums in tab_number)
is
cursor c_main is
select column_value
from table(p_nums)
order by 1;
begin
for r_main in c_main loop
dbms_output.put_line(r_main.column_value);
end loop;
--
if 33 member of p_nums then
dbms_output.put_line('In the list');
end if;
end;
/
exec print_nums(tab_number(10,20,50));
exec print_nums(tab_number(10,20,33));
Obviously, if all parameters you'd like to pass represent the same column value, you can't just list them as if they were two different parameters. If that was the case, procedure should actually name them all, e.g.
create procedure print_emp(par_emp_1 in number, par_emp_2 in number)
but - what if there are 3 or 4 EMPNOs you'd like to pass? You can't modify the procedure every time (not just while declaring it, but also in SELECT statements, as you'd have to add new parameters there as well).
Therefore, one option might be this (as far as I understood the question):
one parameter, whose datatype is varchar2
it means that you'd have to enclose list of EMPNOs into single quotes and separate them with the same separator every time; let it be a comma , sign
cursor's query would contain a subquery (lines #6 - 9) which splits that comma-separated values list of EMPNO values into rows
the rest is simple
Here you go:
SQL> create or replace procedure print_emp(par_empno in varchar2)
2 is
3 begin
4 for c in (select *
5 from emp
6 where empno in (select to_number(regexp_substr(par_empno, '[^,]+', 1, level))
7 from dual
8 connect by level <= regexp_count(par_empno, ',') + 1
9 )
10 )
11 loop
12 dbms_output.put_line(c.empno||' '||c.ename||' '||c.job||' '||c.sal);
13 end loop;
14 end;
15 /
Procedure created.
Scott's EMP sample table:
SQL> select * from emp where deptno = 20;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.1980 00:00:00 1000 20
7566 JONES MANAGER 7839 02.04.1981 00:00:00 2975 20
7788 SCOTT ANALYST 7566 09.12.1982 00:00:00 3000 20
7876 ADAMS CLERK 7788 12.01.1983 00:00:00 1100 20
7902 FORD ANALYST 7566 03.12.1981 00:00:00 3000 20
Testing:
SQL> set serveroutput on
SQL> exec print_emp('7369,7566,7788');
7566 JONES MANAGER 2975
7788 SCOTT ANALYST 3000
7369 SMITH CLERK 1000
PL/SQL procedure successfully completed.
SQL>

How to return a list from a function for a "select... IN.." statement [duplicate]

This question already has answers here:
Function or Procedure for an IN clause
(5 answers)
PL/SQL - Use "List" Variable in Where In Clause
(3 answers)
Closed 3 years ago.
I wish to write a function for an ERP system that returns a list that can be used in a "select...IN" statement. For example...
instead of...
"select * from A where b in ('c','d','e')"
I need something like...
"select * from A where b in (get_items('a'))"
I've tried a function that can create the 'c','d','e' bit, but that doesn't give me any results.
I've tried several methods, but I can't seem to hit upon the right idea. I'm okay with writing API's/functions, but what's the best method to get a list of items out of a function and into that select statement? would an array or a sys refcursor work?
One option is to create a function which returns a table. Here's an example based on Scott's sample schema. The function accepts department number and returns list of empnos in that deptno.
SQL> create or replace function get_emps (par_deptno in number)
2 return sys.odcinumberlist
3 is
4 l_list sys.odcinumberlist := sys.odcinumberlist ();
5 begin
6 select empno
7 bulk collect into l_list
8 from emp
9 where deptno = par_deptno;
10
11 return l_list;
12 end;
13 /
Function created.
Code that looks like yours (with IN):
SQL> select e.empno, e.ename, e.job, e.sal
2 from emp e
3 where e.empno in (select * from table(get_emps(10)));
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7782 CLARK MANAGER 2450
7839 KING PRESIDENT 10000
7934 MILLER CLERK 1300
Code that uses JOIN (the result will be the same):
SQL> select e.empno, e.ename, e.job, e.sal
2 from emp e join table(get_emps(10)) t on e.empno = t.column_value;
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7782 CLARK MANAGER 2450
7839 KING PRESIDENT 10000
7934 MILLER CLERK 1300
SQL>

Resources