In my code there is a function that displays a message on the screen. But when I start, I do not see the result.
I guess this line does not work.
UPDATE EMPLOYEES SET COMMISSION_PCT = NEWCOMMISSION WHERE LINES.COMMISSION_PCT IS NULL;
Is it so?
I'm using Oracle XE 112, I'm using a browser.
Full code
CREATE OR replace PROCEDURE Zadacha31
IS
lengthphone INTEGER;
substrcommission VARCHAR2(50);
newcommission FLOAT;
info FLOAT;
CURSOR get_data IS
SELECT *
FROM employees;
BEGIN
FOR lines IN get_data LOOP
IF lines.commission_pct IS NULL THEN
lengthphone := Length(lines.phone_number);
substrcommission := Substr(lines.phone_number, lengthphone - 1, 2);
newcommission := To_number('.'
||substrcommission);
UPDATE employees
SET commission_pct = newcommission
WHERE lines.commission_pct IS NULL;
dbms_output.Put_line(lines.commission_pct);
END IF;
END LOOP;
END;
Try executing set serveroutput on before you run the procedure
It seems that you have an error in your logic.
Consider. You execute the UPDATE only if this test is true:
IF lines.commission_pct IS NULL THEN
Your code calculates a new commission and updates all the records (hint: this is probably not what you want but is irrelevant to your question).
UPDATE employees
SET commission_pct = newcommission
WHERE lines.commission_pct IS NULL;
However, your output is displaying the unamended original value and nothing else.
dbms_output.Put_line(lines.commission_pct);
So your procuedure may well be updating and displaying something, but all it's displaying is a NULL so it looks as though nothing is occuring.
To solve this, use the new value:
dbms_output.Put_line('new commission = ' ||newcommission);
To solve the updating all records use a better where condition:
UPDATE employees e
SET e.commission_pct = newcommission
WHERE e.emp_id = lines.emp_id; -- or whatever the PK is
And why not make the output message more useful while we're at it?
dbms_output.Put_line('emp# '|| lines.emp_id||' new commission = ' ||newcommission);
This is how it looks like in SQL*Plus:
SQL> CREATE OR replace PROCEDURE Zadacha31
2 IS
3 lengthphone INTEGER;
4 substrcommission VARCHAR2(50);
5 newcommission FLOAT;
6 info FLOAT;
7 CURSOR get_data IS
8 SELECT *
9 FROM employees;
10 BEGIN
11 dbms_output.put_line('Procedure starts here');
12 FOR lines IN get_data LOOP
13 IF lines.commission_pct IS NULL THEN
14 dbms_output.put_line('commision_pct IS NOT NULL!');
15
16 lengthphone := Length(lines.phone_number);
17
18 substrcommission := Substr(lines.phone_number, lengthphone - 1, 2);
19
20 newcommission := To_number('.'
21 ||substrcommission);
22
23 UPDATE employees
24 SET commission_pct = newcommission
25 WHERE lines.commission_pct IS NULL;
26
27 dbms_output.Put_line(lines.commission_pct);
28 END IF;
29 END LOOP;
30 dbms_output.put_line('Procedure ends here');
31 END;
32 /
Procedure created.
SQL>
SQL> set serveroutput on
SQL>
SQL> begin
2 zadacha31;
3 end;
4 /
Procedure starts here
Procedure ends here
PL/SQL procedure successfully completed.
SQL>
It appears that the procedure finished correctly, but - nothing was displayed nor updated. Let's check why:
SQL> select count(*) from employees where commission_pct is null;
COUNT(*)
----------
0
SQL>
Does it ring a bell?
As of your environment: it looks like Apex' SQL Workshop, while data you use is contained in the HR schema. If that's so, you're right - you don't need to SET SERVEROUTPUT ON there, it is enabled by default.
Though, that wasn't your problem, but the fact that no employee has COMMISSION_PCT IS NULL.
Related
I have written some code to print whether the condition is true/false, but I can't get the output as true / false. Why?
Here is that code:
create or replace PROCEDURE TET_STOP_DID
IS
result VARCHAR2(10);
LastGeneratedcode TET_LASTGENERATEDMASKCODE.Maskedcode%TYPE;
BEGIN
select maskedcode into LastGeneratedcode from TET_LASTGENERATEDMASKCODE;
IF (SUBSTR(LastGeneratedcode,5,5) !='ZZZZZ') then
result := 'true';
else
result :='false';
END IF;
END TET_STOP_DID;
You aren't printing anything; you've just set the result variable to some value, and that's it. Though, that procedure doesn't look right as it'll raise too_many_rows error if there are two (or more) rows in the table (or no_data_found if it is empty). I presume you'd want to pass some parameter which restricts number of fetched rows.
For example:
SQL> select * from tet_lastgeneratedmaskcode;
ID MASK
---------- ----
1 abcd
2 fff
Procedure which accepts a parameter and handles exceptions (that's rather poor handling, just to show what to pay attention to):
SQL> create or replace procedure tet_stop_did
2 (par_id in tet_lastgeneratedmaskcode.id%type)
3 is
4 result varchar2(10);
5 lastgeneratedcode tet_lastgeneratedmaskcode.maskedcode%type;
6 begin
7 select maskedcode
8 into lastgeneratedcode
9 from tet_lastgeneratedmaskcode
10 where id = par_id;
11
12 if substr(lastgeneratedcode, 5, 5) != 'ZZZZZ' then
13 result := 'true';
14 else
15 result := 'false';
16 end if;
17
18 dbms_output.put_line('Result = ' || result);
19 exception
20 when no_data_found then
21 dbms_output.put_line('No rows for that ID');
22 when too_many_rows then
23 dbms_output.put_line('Two or more rows for that ID');
24 end tet_stop_did;
25 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec tet_stop_did (1);
Result = false --> here's your result
PL/SQL procedure successfully completed.
SQL>
If you can use SERVEROUTPUT clause to run the procedure you may insert DBMS_OUTPUT in stored procedure code:
CREATE OR REPLACE PROCEDURE TET_STOP_DID
IS
result VARCHAR2(10);
LastGeneratedcode TET_LASTGENERATEDMASKCODE.Maskedcode%TYPE;
BEGIN
select maskedcode into LastGeneratedcode from TET_LASTGENERATEDMASKCODE;
IF (SUBSTR(LastGeneratedcode,5,5) !='ZZZZZ') then
result := 'true';
ELSE
result := 'false';
END IF;
-- Print the output
DBMS_OUTPUT.PUT_LINE(result);
END TET_STOP_DID;
And then from terminal, it runs so:
SQL> SET SERVEROUTPUT ON;
SQL> EXECUTE TET_STOP_DID
false
Thank you
Pretty new to Oracle, need help on this Procedure, using Oracle 11g
Sample table data (TAB1):
ID
Amount
Currency
10
300
GBP
15
500
GBP
20
100
GBP
Requirement is to select all the ID's from TAB1 based on currency and store it in a variable and later use these ID's in other select Queries within the same Stored Procedure.
CREATE OR REPLACE PROCEDURE myproc (i_id IN VARCHAR, i_curr IN VARCHAR)
AS
CURSOR GET_I IS
SELECT ID, CURRENCY
FROM TAB1
WHERE CURRENCY = 'GBP';
-- This will give me 3 ID's (10, 15 & 20) which I am storing in variable r_1 below.
r_1 VARCHAR (5) : NULL;
BEGIN
OPEN GET_I;
LOOP
FETCH GET_I INTO r_1;
IF GET_I%NOTFOUND
THEN
EXIT;
---In the below ELSE PART can we run a select query using the value stored in r_1??
--ELSE
--Data stored in r_1 to be used in further select queries in later part and output of the below
--be returned as SYS_REFCURSOR;
--BELOW two lines gives error
--FOR I in r_1 (
--SELECT ID FROM TAB2 WHERE TAB2.ID=r_1);
END IF;
END LOOP;
CLOSE GET_ID;
END;
It looks you want to use a nested FOR loop.
I'd suggest you to use cursor FOR loops - they are easier to maintain as you don't have to declare cursor variable (by the way, in your case it wouldn't work anyway as you'd like to store both ID and CURRENCY into a scalar R_1 variable), open the cursor, pay attention about exiting the loop and closing the cursor. In a cursor FOR loop, Oracle does all that for you.
Here's an example:
Sample table:
SQL> select * from tab1;
ID AMOUNT CUR
---------- ---------- ---
10 300 GBP
15 500 GBP
20 100 GBP
Procedure:
SQL> create or replace procedure myproc as
2 begin
3 for cur_id in (select id from tab1) loop
4 dbms_output.put_line('ID = ' || cur_id.id);
5 for cur_other in (select amount, currency
6 from tab1
7 where id = cur_id.id --> use ID fetched in outer loop
8 )
9 loop
10 dbms_output.put_line(cur_other.amount ||' - '|| cur_other.currency);
11 end loop;
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec myproc;
ID = 10
300 - GBP
ID = 15
500 - GBP
ID = 20
100 - GBP
PL/SQL procedure successfully completed.
SQL>
How to return a refcursor?
SQL> create or replace procedure myproc (par_id in tab1.id%type,
2 par_rc out sys_refcursor) as
3 begin
4 for cur_id in (select id
5 from tab1
6 where id = par_id
7 ) loop
8
9 open par_rc for select amount, currency
10 from tab1
11 where id = cur_id.id;
12 end loop;
13 end;
14 /
Procedure created.
SQL> var l_rc refcursor
SQL>
SQL> exec myproc(10, :l_rc);
PL/SQL procedure successfully completed.
SQL> print l_rc
AMOUNT CUR
---------- ---
300 GBP
SQL>
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>
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!!
Can I access a cursor's column dynamically? I mean by name? something like this:
declare
v_cursor := select * from emp;
begin
FOR reg IN v_cursor LOOP
dbms_output.put_line(**reg['column_name_as_string']**);
end loop;
end;
I know the bold part is not PL/SQL, but I'm looking for something like that and can't find it anywhere.
You can use the package DBMS_SQL to create and access cursors with dynamic queries.
However it's not really straightforward to access a column by name because the DBMS_SQL package uses positioning and in a dynamic query we may not know the order of the columns before the execution.
Furthermore, in the context of this question, it appears that we may not know which column we want to display at compile time, we will assume that the column we want to display is given as a parameter.
We can use DBMS_SQL.describe_columns to analyze the columns of a SELECT query after it has been parsed to build a dynamic mapping of the columns. We will assume that all columns can be cast into VARCHAR2 since we want to display them with DBMS_OUTPUT.
Here's an example:
SQL> CREATE OR REPLACE PROCEDURE display_query_column(p_query VARCHAR2,
2 p_column VARCHAR2) IS
3 l_cursor INTEGER;
4 l_dummy NUMBER;
5 l_description_table dbms_sql.desc_tab3;
6 TYPE column_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(32767);
7 l_mapping_table column_map_type;
8 l_column_value VARCHAR2(4000);
9 BEGIN
10 l_cursor := dbms_sql.open_cursor;
11 dbms_sql.parse(l_cursor, p_query, dbms_sql.native);
12 -- we build the column mapping
13 dbms_sql.describe_columns3(l_cursor, l_dummy, l_description_table);
14 FOR i IN 1 .. l_description_table.count LOOP
15 l_mapping_table(l_description_table(i).col_name) := i;
16 dbms_sql.define_column(l_cursor, i, l_column_value, 4000);
17 END LOOP;
18 -- main execution loop
19 l_dummy := dbms_sql.execute(l_cursor);
20 LOOP
21 EXIT WHEN dbms_sql.fetch_rows(l_cursor) <= 0;
22 dbms_sql.column_value(l_cursor, l_mapping_table(p_column), l_column_value);
23 dbms_output.put_line(l_column_value);
24 END LOOP;
25 dbms_sql.close_cursor(l_cursor);
26 END;
27 /
Procedure created
We can call this procedure with a query known only at run-time:
SQL> set serveroutput on
SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'ENAME');
SMITH
ALLEN
WARD
JONES
PL/SQL procedure successfully completed
SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'EMPNO');
7369
7499
7521
7566
PL/SQL procedure successfully completed
Use caution with dynamic SQL: it has the same privileges as the user and can therefore execute any DML and DDL statement allowed for this schema.
For instance, the above procedure could be used to create or drop a table:
SQL> exec display_query_column('CREATE TABLE foo(id number)', '');
begin display_query_column('CREATE TABLE foo(id number)', ''); end;
ORA-01003: aucune instruction analysée
ORA-06512: à "SYS.DBMS_SQL", ligne 1998
ORA-06512: à "APPS.DISPLAY_QUERY_COLUMN", ligne 13
ORA-06512: à ligne 1
SQL> desc foo
Name Type Nullable Default Comments
---- ------ -------- ------- --------
ID NUMBER Y
It's probably easiest to make the query dynamic if you can.
DECLARE
v_cursor SYS_REFCURSOR;
dynamic_column_name VARCHAR2(30) := 'DUMMY';
column_value VARCHAR2(32767);
BEGIN
OPEN v_cursor FOR 'SELECT ' || dynamic_column_name || ' FROM dual';
LOOP
FETCH v_cursor INTO column_value;
EXIT WHEN v_cursor%NOTFOUND;
dbms_output.put_line( column_value );
END LOOP;
CLOSE v_cursor;
END;
If you really want to have a hardcoded SELECT * and dynamically select a column from that by name, I think you could do that using DBMS_SQL as Vincent suggests, but it will be somewhat more complex.
You mean something like:
declare
cursor sel_cur is
select * from someTable;
begin
for rec in sel_cur
loop
dbms_output.put_line('col1: ' || rec.col1);
end loop;
end;