how do I use if condition in cursor because our professor don't allow us use where clause in the select statement - oracle

Our question is showing all the countries that have names that are exactly 5 letter long. This is the cursor code and I want add if condition into it.
declare
cursor cursor_full is select * from country_cont;
begin
for counter in cursor_full
loop
dbms_output.put_line(counter.country|| ' ' || counter.continent);
end loop;
end;
However my professor said that you can't using where clause within the select statement and you should display all the countries and continent.
so i tried this code:
declare
country varchar(50);
cursor cursor_full is select * from country_cont;
begin
if length(country)=5 then
for counter in cursor_full
loop
dbms_output.put_line(counter.country|| ' ' || counter.continent);
end loop;
end if;
end;
the script output show that PL/SQL procedure successfully completed but nothing return in DBMS output
Hope someone can help me, I spent whole night to think about it,please!

Variable country doesn't contain any value, it is null so if condition is never true and loop is never executed. Sample data would help; meanwhile, see if this helps.
begin
for cur_r in (select * from country_cont) loop
if length(cur_r.country) > 5 then
dbms_output.put_line(cur_r.country|| ' ' || cur_r.continent);
end loop;
end;
Don't forget to set serveroutput on.

Related

My long time SQL*Plus loop doesn't print DBMS_OUTPUT.PUT_LINE output during execution

I know that in order to print something on sqlplus like below:
begin
dbms_output.put_line('Hello!');
end;
/
I need to call
set serveroutput on;
before that.
I also know that is not needed, but I can also call
DBMS_OUTPUT.enable;
before, just in case. This is working for me.
But what if I want to keep printing the progress of a long loop? It seems impossible to me. I've tried everything to print some progress on the loop below but just doesn't work. Is there some way of doing that? I even tried to spool to a file and didn't work.
Note 1: I can't truncate or partition this table as the DBA doesn't want to help me with that, so I have to use this nasty loop...
Note 2: I've noticed that once the loop is done, the whole output is printed. Looks like oracle is buffering the output and printing everything at the end. I'm not sure how to avoid that and print on every loop iteration.
set serveroutput on;
declare
e number;
i number;
nCount number;
f number;
begin
DBMS_OUTPUT.enable;
dbms_output.put_line('Hello!');
select count(*) into e from my_big_table where upd_dt < to_date(sysdate-64);
f :=trunc(e/10000)+1;
for i in 1..f
loop
delete from my_big_table where upd_dt < to_date(sysdate-64) and rownum<=10000;
commit;
DBMS_OUTPUT.PUT_LINE('Progress: ' || to_char(i) || ' out of ' || to_char(f));
end loop;
end;
Thank you for any answer.
There are 2 standard ways for such things:
set module and action in your session DBMS_APPLICATION_INFO.SET_MODULE:
SQL> exec DBMS_APPLICATION_INFO.SET_MODULE('my_long_process', '1 from 100');
PL/SQL procedure successfully completed.
SQL> select action from v$session where module='my_long_process';
ACTION
----------------------------------------------------------------
1 from 100
set session_longops:
DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS
I'd recommend it in your case since that is exactly designed for long operations.
Example on Oracle-Base.
----
PS: dbms_output,put_line saves all output in a collection (nested table) variable of dbms_output package, so you can't get it from another session and client can't get it during user call (execution). In addition to set serveroutput on you can also get the output using dbms_output.get_lines: http://orasql.org/2017/12/10/sqlplus-tips-8-dbms_output-without-serveroutput-on/
Btw, in case if you need to filter or analyze output from dbms_output, sometimes it's convenient to get output in a query, so you can use filter strings in where clause or aggregate them: https://gist.github.com/xtender/aa12b537d3884f4ba82eb37db1c93c25
DBMS_OUTPUT will only ever be displayed after the PL/SQL code has terminated and control has returned to the calling program.
Output is, as you found, buffered. When your PL/SQL code finishes, then the calling program (e.g. SQL*Plus) can go and fetch that output.
Insert into another table, maybe call it "MYOUTPUT".
Create the table:
create table myoutput (lineno number, outline varchar2(80));
Add this after your delete:
insert into MYOUTPUT values (i,'Progress: ' || to_char(i) || ' out of ' || to_char(f));
Then select from MYOUTPUT periodically to see progress.
select outline from myoutput order by lineno;
Bobby
You can use UTL_FILE to write output to an external file, as in:
DECLARE
fh UTL_FILE.FILE_TYPE;
nRow_count NUMBER := 0;
BEGIN
fh := UTL_FILE.FOPEN('DIRECTORY_NAME', 'some_file.txt', 'w');
FOR aRow IN (SELECT *
FROM SOME_TABLE)
LOOP
nRow_count := nRow_count + 1;
IF nRow_count MOD 1000 = 0 THEN
UTL_FILE.PUT_LINE(fh, 'Processing row ' || nRow_count);
UTL_FILE.FFLUSH(fh);
END IF;
-- Do something useful with the data in aRow
END LOOP; -- aRow
UTL_FILE.FCLOSE_ALL; -- Close all open file handles, including
-- the ones I've forgotten about...
END;

Multiple line output in pl/sql

I have a PL/SQL file that has a loop structure.
The script is as follows.
SET SERVEROUTPUT ON
declare
c_id employee.id%type;
c_name employee.name%type;
c_address employee.address%type;
CURSOR c_employee is
SELECT id, name, address from employee;
begin
open c_employee;
LOOP
FETCH c_employee into c_id, c_name, c_address;
EXIT when c_employee%notfound;
dbms_output.put_line(c_id||' '||c_name||' '||c_address);
END LOOP;
close c_employee;
end;
/
When I run this from SQLPlus I get only the details of the first row but not the rest. What am I doing wrong? How to get all the outputs for the loop.
Try to convert your code to use a for loop instead of the open statement, like so -
for r_employee in c_employee
LOOP
dbms_output.put_line(r_employee.c_id||' '||r_employee.c_name||' '||r_employee.c_address);
END LOOP;
Where r_employee is a variable of employee%type.
The way you currently wrote it does not iterate through the cursor, and this is why only the first row is presented.
Even though your code looks correct, it should iterate through all the
row not just one. Try to use below snippet and run it in SQL plus if
still single row then there may be some other issue.
SET SERVEROUTPUT ON
DECLARE
BEGIN
FOR I IN
(SELECT id, name, address FROM employee
)
LOOP
dbms_output.put_line(I.ID||' '||I.name||' '||I.address);
END LOOP;
END;
/

If condition in PL/SQL script with cursor and loop

I would like to ask for some help or advice in this particular case.
I have table called "Teams". The table contains 3 columns - Issue, Responsible_team and More_Info (all varchar2).
I have a PL/SQL script with cursor and loop for selecting as many teams as issue description you type (some random word you think it might help you find the responsible team). This part works great for me.
But I do not know how to compile the IF condition in there. If no team is found according to typed word description, I would like to get some basic output dbms_output.put_line('Responsible team is not found').
There are 2 ways how I wrote the script. Classic loop and while loop.
I would be happy for any advice.
1.script
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
--cursor declaration
CURSOR c_respteam
RETURN teams%ROWTYPE
IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
LOOP
FETCH c_respteam into v_issue, v_respteam, v_info;
EXIT when c_respteam%NOTFOUND;
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
END LOOP;
CLOSE c_respteam;
end;
/
2.script
-- cursor with while loop
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
CURSOR c_respteam
RETURN teams%ROWTYPE IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
FETCH c_respteam INTO v_issue, v_respteam, v_info;
WHILE c_respteam%FOUND
LOOP
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
FETCH c_respteam INTO v_issue, v_respteam, v_info;
END LOOP;
CLOSE c_respteam;
END;
/
You could rewrite to:
declare
l_found boolean :=false;
cursor c_respteam is
select issue
,responsible_team
,more_info
from teams
where lower(issue) like '%&Describe_Issue%';
begin
for r in c_respteam
loop
l_found := true;
dbms_output.put_line('Responsible team is ' || r.responsible_team || ' --> ' || r.more_info);
end loop;
if not l_found
then
dbms_output.put_line('No records found');
end if;
end;
/
You need to have a counter variable [ETA: ooh, I like Rene's boolean variable idea instead; either way, you need an extra variable!] to work out if any rows were returned or not. I'm not sure why you're using an explicit cursor fetch, rather than using the cursor-for-loop? Cursor-for-loops are not only easier to write, read and maintain, but Oracle have put some behind-the-scenes optimisation in, to aid performance.
Of course, depending on what you're actually doing with the data returned by your cursor (dbms_output.put_line being something that you should never have in your production code), it's debatable that you would need to loop through a cursor at all.
Anyway, with that said, here's an example demonstrating how I would handle your requirement to check for no rows returned by the cursor:
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('Y')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
no rows returned
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('X')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
value of dummy = X
To expand on what I said in my comment below, it sounds like you just need a single sql statement, rather than using PL/SQL and relying on dbms_output.
Eg., say you have the following statement:
select lvl
from (select 'X'||level lvl from dual connect by level <= 10)
where lvl like '%&val%';
with &val blank, you get:
LVL
-----------------------------------------
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
With &val = 2 you get:
LVL
-----------------------------------------
X2
With &val = 100 you get:
no rows selected.

Creating a result set (using a select statement ) within a loop

I have created a cursor which returns me a set of rows. While iterating through each of the row, I want to get another result set (by forming a SELECT statement by with a WHERE clause having value from the processed row) from another table. I am a newbie in PLSQL. Can you please guide me on how this could be done? (Can we have a Cursor defined inside the loop while looping for the resultset of the cursor)?
Please excuse me if I am not able to make myself clear.
Thanks in advance
DECLARE
CURSOR receipts IS
SELECT CREATED_T, ACCT_NO, AMT FROM receipt_t
WHERE OBJ_TYPE='misc';
receipts_rec receipts%ROWTYPE;
BEGIN
-- Open the cursor for processing
IF NOT receipts%ISOPEN THEN
OPEN receipts;
END IF;
LOOP
FETCH receipts INTO receipts_rec;
EXIT WHEN receipts%NOTFOUND;
/* Loop through each of row and get the result set from another table */
newQuery := 'SELECT * FROM ageing_data WHERE ACCT_NO = ' || receipts_rec.ACCT_NO;
-- Execute the above query and get the result set, say RS
LOOP
-- For above result set-RS
END LOOP;
END LOOP;
CLOSE receipts;
END;
Yes, you can define a cursor that takes a set of parameters and use those values in the WHERE clause.
DECLARE
CURSOR c_cursor1 IS
SELECT field1, field2, ... , fieldN
FROM table1
WHERE conditions;
CURSOR c_cursor2 (p_parameter NUMBER) IS
SELECT field1, field2, ..., fieldN
FROM table2
WHERE table2.field1 = p_parameter;
BEGIN
FOR record1 IN c_cursor1 LOOP
FOR record2 IN c_cursor2(record1.field1) LOOP
dbms_output.put_line('cursor 2: ' || record2.field1);
END LOOP
END LOOP;
END;
Yes, you can do that, but there is absolutely no reason to. Try the following:
BEGIN
FOR aRow IN (SELECT rt.CREATED_T, rt.ACCT_NO, rt.AMT, ad.*
FROM RECEIPT_T rt
INNER JOIN AGEING_DATA ad
ON (ad.ACCT_NO = rt.ACCT_NO)
WHERE rt.OBJ_TYPE='misc')
LOOP
-- Process the data in aRow here
END LOOP;
END;
This does exactly the same work as the original "loop-in-a-loop" structure but uses the database to join the tables together on the common criteria instead of opening and closing cursors multiple times.
Share and enjoy.
Something like this can be done in the following manner:
DECLARE
CURSOR cursor1 IS
SELECT *
FROM table1;
CURSOR cursor2 IS
SELECT *
FROM table2
WHERE column1 = I_input_param;
BEGIN
FOR table_1_rec in cursor1 LOOP
I_input_param := table_1_rec.column_1;
FOR table_2_rec in cursor2 LOOP
....
....
END LOOP;
END LOOP;
END;
I have used an implicit open/fetch here. I hope you get the idea.

pl/sql loop records select oracle plsql

I have a select statement that I am trying to loop over and increment a variable based on the condition of the select statement, then return the variable as an out so I can do something with it in some front end code. I am using oracle 11g and I am seeing a few ways I can do this... but I am not sure which is the best way. I have some of what I am trying to do below, but again stopped because of confusion.
First I am setting my proc and 'in variable'
PROCEDURE SEEKER (pMonkeyID IN Number, vMarkCounter OUT Number)
AS
BEGIN
CURSOR seeker_cur IS
Select Mokney_approved, Monkey_vaulted
from MonkeyBookApps
where MonkeyID = pMonkeyID
and Monkey_doc_type = 'BANANA'
order by docu_approv_timestamp,monkey_doc_type,monkey_doc_approved desc
OPEN seeker_cur;
begin
OPEN Seeker_cur;
vMarkCounter := 0;
Here is the part I am not sure about. Should I loop and then exit if the condition is not met or should I do an if statement and somehow determine if there is a record that could be greater than one? If so how would that work? Is there a benefit to doing one way over the other? So... I am going to sudo-code what I am trying to do (below):
FOR (however many records) in Seeker_cur
IF seeker_cur (not found) or (returns no records)
EXIT or (break for loop);
ELSE
LOOP
vMarkCounter := vMarkCounter + 1;
EXIT WHEN seeker_cur is out of records (somehow)
END IF;
END LOOP;
END;
END SEEKER;
I am sure there are a few ways to do this. What ways would you suggest?
why dont you use implicit cursor , it will open and close itself:
DECLARE
CURSOR seeker_cur IS
Select Mokney_approved, Monkey_vaulted
from MonkeyBookApps
where MonkeyID = pMonkeyID
and Monkey_doc_type = 'BANANA'
order by docu_approv_timestamp,monkey_doc_type,monkey_doc_approved desc;
vMarkCounter number:=0;
BEGIN
FOR i IN seeker_cur
LOOP
vMarkCounter := vMarkCounter+1;
END LOOP;
dbms_output.put_line(vMarkCounter);
END;
It seems to me that the solution your problem might be as simple as this:
SELECT COUNT(*)
INTO l_some_local_variable
FROM monkey_book_apps
WHERE monkey_id = p_monkey_id
AND monkey_doc_type = 'BANANA';
RETURN l_some_local_variable;
Avoiding PL/SQL loops and using the simplest SQL possible is (almost always) the most efficient way. Tom Kyte calls the row-by-row execution of LOOPs "slow-by-slow".

Resources