PL/ SQL- WHEN / Conditional commands - oracle

I am trying to increase the salary of three employees by prompting the user. The job_ids are 1, 4, and 8. I cannot seem to get my WHEN statements to work or properly interact with the datatables. Anyone have any insights?
SET SERVEROUTPUT ON
SET VERIFY OFF
accept selection prompt 'Please enter your employee ID'
DECLARE
JobNumber INT(2) := ('&selection.');
NewSalary VARCHAR2(30);
BEGIN
update emp_employee
set NewSalary = case
WHEN JobNumber = '1' THEN NewSalary := emp_employees.salary * 1.10;
WHEN JobNumber = '4' THEN NewSalary := emp_employees.salary * 1.15;
WHEN JobNumber = '8' THEN NewSalary := emp_employees.salary * 1.20;
ELSE 'Invalid ID'
END;
DBMS_OUTPUT.PUT_LINE ('Job ID '|| JobNumber || 'Salary ' || NewSalary);
END;
/

i think your update should be like below
update emp_employee
set NewSalary = case
WHEN JobNumber = 1 THEN emp_employees.salary * 1.10;
WHEN JobNumber = 4 THEN emp_employees.salary * 1.15;
WHEN JobNumber = 8 THEN emp_employees.salary * 1.20;
ELSE null
END;
//NewSalary is numeric field you can not assign string value invalid

As you didn't provide test case, I'm using Scott's schema and his EMP table. The principle is the same, though.
Have a look a this example and note the differences:
CASE you wrote is wrong; you've already said that NewSalary should be something (within the UPDATE), so you don't repeat it once again)
ELSE can't be 'Invalid ID'; you'd put a string into a NUMBER datatype column. I handled it within the IF statement - if nothing has been updated, such an employee doesn't exist.
you can't output NewSalary as you don't know it. I returned its value into a separate local variable (l_new_sal)
Also, if I were you, I'd rather create a stored procedure instead of using an anonymous PL/SQL block. If you use it for educational purposes, let it be (but you can try to convert it to a procedure, if you want).
Here you go; one example with a non-existent EMPNO, and another one for employee that exists.
SQL> set serveroutput on
SQL> set ver off
SQL> accept par_selection prompt 'Enter EMPNO: '
Enter EMPNO: 1234
SQL> declare
2 l_empno emp.empno%type := &par_selection; -- it is employee ID, not job number!
3 l_new_sal emp.sal%type;
4 l_cnt number;
5 begin
6 update emp e set
7 e.sal = case when e.deptno = 10 then e.sal * 1.1
8 when e.deptno = 20 then e.sal * 1.15
9 when e.deptno = 30 then e.sal * 1.2
10 else e.sal
11 end
12 where e.empno = l_empno
13 returning sal into l_new_sal;
14
15 l_cnt := sql%rowcount;
16
17 if l_cnt > 0 then
18 dbms_output.put_line('Employee ' || l_empno || ', new salary = ' || l_new_sal);
19 else
20 dbms_output.put_line('Nobody got new salary');
21 end if;
22 end;
23 /
Nobody got new salary
PL/SQL procedure successfully completed.
SQL> accept par_selection prompt 'Enter EMPNO: '
Enter EMPNO: 7369
SQL> /
Employee 7369, new salary = 920
PL/SQL procedure successfully completed.
SQL>

Related

PL/SQL simple task

I want to create a procedure whith two arguments. It should check if the arguments are values of family table and if both are not the same value.
I planned this code but I noted it doesn't work
create or replace procedure Compare(first_value values.value%type, second_value values.value%type)
as
begin
if second_value not exists(select values from family) then
dbms.output.put_line('The second values doesn't exist');
else if first_value = second_value then
dbms.output.put_line('Both values are the same');
else
dbms.output.put_line('Great Job');
end if;
end;
/
I'll appreciate any help.
Here's one option.
SQL> set serveroutput on
Sample data:
SQL> select * from family;
C_VALUES
--------
Little
Foot
Procedure:
SQL> create or replace procedure p_compare
2 (par_value_1 in family.c_values%type,
3 par_value_2 in family.c_values%type
4 )
5 as
6 l_cnt_1 number;
7 l_cnt_2 number;
8 begin
9 select count(*)
10 into l_cnt_1
11 from family
12 where c_values = par_value_1;
13
14 select count(*)
15 into l_cnt_2
16 from family
17 where c_values = par_value_2;
18
19 if l_cnt_1 = 0 then
20 dbms_output.put_line('The first value does not exist');
21 elsif l_cnt_2 = 0 then
22 dbms_output.put_line('The second value does not exist');
23 elsif par_value_1 = par_value_2 then
24 dbms_output.put_line('Both values are the same');
25 else
26 dbms_output.put_line('Great job');
27 end if;
28 end;
29 /
Procedure created.
Testing:
SQL> exec p_compare('Little', 'Foot');
Great job
PL/SQL procedure successfully completed.
SQL> exec p_compare('Little', 'Little');
Both values are the same
PL/SQL procedure successfully completed.
SQL> exec p_compare('Big', 'Foot');
The first value does not exist
PL/SQL procedure successfully completed.
SQL>
Here is a not so well written, but somehow alternative take on the task:
CREATE OR REPLACE PROCEDURE COMPARE(FIRST_VALUE VALUES.VALUE%TYPE,
SECOND_VALUE VALUES.VALUE%TYPE)
AS
l_message VARCHAR2(40);
BEGIN
SELECT CASE WHEN test_val < 2 THEN 'The first value does not exist'
WHEN test_val < 3 THEN 'The second value does not exist'
WHEN first_value = second_value THEN 'Both values are the same'
ELSE 'Great job'
END
INTO l_message
FROM (SELECT NVL(SUM(val),0) AS test_val
FROM (SELECT 2 AS val
FROM family
WHERE val = first_value
AND ROWNUM = 1
UNION ALL
SELECT 1 AS val
FROM family
WHERE val = second_value
AND ROWNUM = 1));
DBMS_OUTPUT.PUT_LINE(l_message);
END;
/

How to update ref cursor values in oracle?

I want to fetch limited no. of rows using refcursor. then I need to update same set of records. is it possible?
create or replace PROCEDURE myproc (
P_ROWCOUNT IN NUMBER,
OUT_TXN_IDS OUT OF_CR_TYPE,
P_CD_ERROR OUT NUMBER,
P_DS_ERROR OUT VARCHAR2
)
AS
V_TXNID NUMBER;
BEGIN
P_CD_ERROR := 0;
P_DS_ERROR := 'SUCCESS';
OPEN OUT_TXN_IDS for
SELECT id FROM table1 WHERE status='N' AND ROWNUM<=P_ROWCOUNT;
EXCEPTION
WHEN OTHERS THEN
P_CD_ERROR := sqlcode;
P_DS_ERROR := 'WF-ERROR - myproc - ' || substr(SQLERRM, 1, 200);
RETURN;
END myproc;
I need to update same records to status Y after refcursor returns. can we do this. please suggest
I don't have your tables nor data so I simplified it a little bit, but - it should work nonetheless.
Initial statuses:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 7
N 7
Procedure: basically, you'd modify rows represented by ID returned by ref cursor.
SQL> DECLARE
2 out_txn_ids SYS_REFCURSOR;
3 p_rowcount NUMBER := 5;
4 l_id table1.id%TYPE;
5 BEGIN
6 OPEN out_txn_ids FOR SELECT id
7 FROM table1
8 WHERE status = 'N'
9 AND ROWNUM <= p_rowcount;
10
11 LOOP
12 FETCH out_txn_ids INTO l_id;
13
14 EXIT WHEN out_txn_ids%NOTFOUND;
15
16 UPDATE table1
17 SET status = 'Y'
18 WHERE id = l_id;
19 END LOOP;
20
21 CLOSE out_txn_ids;
22 END;
23 /
PL/SQL procedure successfully completed.
Result:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 12
N 2
SQL>

Oracle Dynamically call cursor

I've two cursors both have almost similar code just a little difference in the group by condition, I want if the id is 1 or 2 e.t.c then it should open cur1 else cur2, basically one cursor at a time. Is there any possibility of achieving this?
if id in (1,2,3,4) then
cursor_value:= 'cur1';
else
cursor_value := 'cur2';
end if;
for i in cursor_value loop
end loop;
You can use an OPEN-FOR statement. Example:
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
/
One option would be to include both cursor's SELECT statements into cursor FOR loop, along with a condition that chooses which one of them will be used. For example:
SQL> declare
2 id number := &par_id;
3 begin
4 for cur_r in (select * from emp
5 where deptno = 10
6 and id in (1,2,3,4)
7 union all
8 select * from emp
9 where deptno = 20
10 and id not in (1,2,3,4)
11 )
12 loop
13 dbms_output.put_Line(cur_r.deptno ||' '||cur_r.ename);
14 end loop;
15 end;
16 /
Enter value for par_id: 1 --> ID = 1, so use SELECT for DEPTNO = 10
old 2: id number := &par_id;
new 2: id number := 1;
10 CLARK
10 KING
10 MILLER
PL/SQL procedure successfully completed.
SQL> /
Enter value for par_id: 823 --> ID <> 1, so use SELECT for DEPTNO = 20
old 2: id number := &par_id;
new 2: id number := 823;
20 SMITH
20 JONES
20 SCOTT
20 ADAMS
20 FORD
PL/SQL procedure successfully completed.
SQL>
The simple way could be to use if else condition.
DECLARE
type cur REF CURSOR;
c cur;
BEGIN
IF id in (1,2,3,4) THEN
OPEN c FOR 'cursor query 1';
ELSE
OPEN c FOR 'cursor query 2';
END IF ;
END;
Cheers!!

how to get the 2nd row and the last row of a file using UTL_FILE

I have a file which consists of thousand rows and I need to get a portion of the 2nd row (about 50 characters) and the last row of the file. Please advise. Thank you.
Im trying to do something like UTL_FILE.READLINE(fileloc, filename, nrow, lastrow).
SAMPLE:
Filename: CLOSED_SO_20190124.txt
DATA in the FILE:
0246608377|22795124004|
650930363|1-8IGO3S82920|
0245563264|22669075004|
0245563264|22669075004|
164260364|1-2DFE-6573219|
650821459|1-6HWQUF11209|
EXPECTED OUTPUT:
650930363|1-8IGO3S82920|
650821459|1-6HWQUF11209|
Here's an example.
Directory name & its location, as well as sample file contents:
SQL> select directory_name, directory_path from all_directories;
DIRECTORY_NAME DIRECTORY_PATH
------------------------------ --------------------
EXT_DIR c:\temp
SQL> $type c:\temp\sofile.txt
0246608377|22795124004|
650930363|1-8IGO3S82920|
0245563264|22669075004|
0245563264|22669075004|
164260364|1-2DFE-6573219|
650821459|1-6HWQUF11209|
SQL>
The procedure: a local counter (l_cnt) knows line number; if it is equal to 2, display that line. Also, when nothing's being found (so exception handler section is executed), I've reached the end of the file so I'm displaying the last line as well.
SQL> set serveroutput on
SQL>
SQL> declare
2 l_file utl_file.file_type;
3 l_dir varchar2(20) := 'EXT_DIR';
4 l_name varchar2(20) := 'sofile.txt';
5 l_line varchar2(50);
6 l_cnt number := 0;
7 begin
8 l_file := utl_file.fopen (l_dir, l_name, 'R');
9 loop
10 begin
11 utl_file.get_line(l_file, l_line);
12 l_cnt := l_cnt + 1;
13 if l_cnt = 2 then
14 dbms_output.put_line('2nd : ' || l_line);
15 end if;
16 exception
17 when no_data_found then
18 dbms_output.put_line('last: ' || l_line);
19 exit;
20 end;
21 end loop;
22 utl_file.fclose(l_file);
23 end;
24 /
2nd : 650930363|1-8IGO3S82920|
last: 650821459|1-6HWQUF11209|
PL/SQL procedure successfully completed.
SQL>
In Oracle 11g
select col
from
(
select
rownum AS rnum,
LEFT(myCol, 50) col
from Table1
where Rownum < 3
)
WHERE rnum = 2
In Oracle 12c
select LEFT(mycol, 50) col
from Table1
ORDER BY val
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
For the last row
select LEFT(mycol, 50) col
from my_table
where pk = ( select max(pk) from my_table )
then you can union them

Run query dynamically in stored procedure in Oracle

Select all the tables of database where column match than pass table name to next query using loop. If column name and column values matches than return true and exist for loop using a stored procedure:
CREATE OR REPLACE PROCEDURE TEST
(
NAME IN VARCHAR2 ,
ID IN NUMBER,
RE OUT SYS_REFCURSOR
) AS
BEGIN
OPEN RE FOR SELECT A.TABLE_NAME FROM
user_tables A JOIN user_tab_columns C
ON C.TABLE_NAME = A.TABLE_NAME
WHERE C.COLUMN_NAME = NAME;
FOR RE IN LOOP
v_Sql := 'SELECT COUNT(*) FROM '|| LOOP.TABLE_NAME || 'WHERE COLUMN_NAME =
ID';
EXECUTE IMMEDIATE v_Sql
IF v_Sql%ROWCOUNT > 0 THEN
return true;
EXIT
END LOOP;
END TEST;
For more understanding the problem
//Get all the tables of database where campus_id is exist in any table of
database
Campus, Class, Section (3 tables found)
Apply forloop on the records
Select count(campus_id) as total from (table name using loop) where campus_id = 1(value
pass)
if(total > 0){
Exist for loop and return true
}
else{
Again iterate the loop to next value
}
What you described doesn't make much sense. If there are several tables that contain a column you're checking and you exit the loop as soon as you find the first one, what about the rest of them?
Here's what I'd do, see if it helps. I'll create a function (not a procedure) that returns a table. In order to do that, I'll create type(s) first:
SQL> create or replace type t_record as object (tn varchar2(30), cnt number);
2 /
Type created.
SQL> create or replace type t_table as table of t_record;
2 /
Type created.
SQL>
The function:
in a cursor FOR loop I'm selecting tables that contain that column
L_STR is used to compose the SELECT statement
DBMS_OUTPUT.PUT_LINE is used to display it first, so that I could visually check whether it is correctly set or not.
if it is, I'm running it with the EXECUTE IMMEDIATE
the result is stored into a table type and returned to the caller
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return t_table
6 is
7 retval t_table := t_table();
8 l_str varchar2(200);
9 l_cnt number;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 retval.extend;
23 retval(retval.count) := t_record(cur_r.table_name, l_cnt);
24 end loop;
25 return retval;
26 end;
27 /
Function created.
Testing:
SQL> select * from table (f_colname('DEPTNO', '10'));
TN CNT
------------------------------ ----------
TEST_201812 1
DEPT 1
EMP 3
SQL> select * from table (f_colname('ENAME', 'KING'));
TN CNT
------------------------------ ----------
EMP 1
BONUS 1
SQL>
That won't work properly for some datatypes (such as DATE) and will have to be adjusted, if necessary.
[EDIT: after you edited the question]
OK then, that's even simpler. It should still be a function (that returns a Boolean, as you said that - in case that something's being found - you want to return TRUE). Code is pretty much similar to the previous function.
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return boolean
6 is
7 l_str varchar2(200);
8 l_cnt number;
9 retval boolean := false;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 if l_cnt > 0 then
23 retval := true;
24 exit;
25 end if;
26 end loop;
27 return retval;
28 end;
29 /
Function created.
Testing: as you can't return Boolean at SQL layer, you have to use an anonymous PL/SQL block, as follows:
SQL> declare
2 l_ret boolean;
3 begin
4 if f_colname('DEPTNO', '15') then
5 dbms_output.put_line('It exists');
6 else
7 dbms_output.put_line('It does not exist');
8 end if;
9 end;
10 /
It does not exist
PL/SQL procedure successfully completed.
SQL>

Resources