NO_DATA_FOUND EXCEPTION in two cursors - Oracle PL/SQL - sql

I need to be able to raise an exception as the title says. The exception I have currently gives me this error:
DBMS_OUTPUT.PUT_LINE (‘No rows found’);
*
ERROR at line 39:
ORA-06550: line 39, column 23:
PLS-00103: Encountered the symbol "`" when expecting one of the following:
( ) - + case mod new not null others <an identifier>
<a double-quoted delimited-identifier> <a bind variable>
table avg count current exists max min prior sql stddev sum
variance execute multiset the both leading trailing forall
merge year month DAY_ hour minute second timezone_hour
timezone_minute timezone_region timezone_abbr time timestamp
interval date
<a string literal with character set specification>
This is my code:
SET SERVEROUTPUT ON FORMAT WRAP SIZE 12000
Declare
v_model VARCHAR2(40);
v_carcategory VARCHAR2(40);
v_totalcars NUMBER;
v_maxdate DATE:=TO_DATE(1, 'J');
Cursor carcur IS
SELECT * FROM i_car;
CURSOR c1(v_car_registration VARCHAR2) IS
SELECT * from i_booking a
WHERE a.registration=v_car_registration;
Begin
For car_rec in carcur
LOOP
v_maxdate:=TO_DATE(1, 'J');
for rec in c1(car_rec.registration)
LOOP
IF rec.date_reserved > v_maxdate
then
v_maxdate:=rec.date_reserved ;
If car_rec.Cost <=50000 THEN v_carcategory := 'Budget Car';
End IF;
If car_rec.Cost BETWEEN 50000 AND 100000 THEN v_carcategory := 'Standard Car';
End IF;
If car_rec.Cost >=100000 THEN v_carcategory := 'Premium Car';
End If;
end IF;
v_totalcars := findtotalcarmodels (car_rec.model_name);
end loop;
DBMS_OUTPUT.PUT_LINE('Registration:'|| ' '|| car_rec.registration);
DBMS_OUTPUT.PUT_LINE('Cost:'|| ' $' || car_rec.Cost);
DBMS_OUTPUT.PUT_LINE('Model Name:'|| ' '|| car_rec.model_name);
DBMS_OUTPUT.PUT_LINE('Car Category:'|| ' '||v_carcategory);
DBMS_OUTPUT.PUT_LINE('Total number of Cars:'|| ' '||v_totalcars);
DBMS_OUTPUT.PUT_LINE('Most Recent Rental Date: '|| ' '||v_maxdate);
DBMS_OUTPUT.NEW_LINE;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE (‘No rows found’);
END;
/
I am not overly sure about the positioning of my exception. Any advice would be much appreciated.

The immediate problem is that your dbms_output.put_line call is using Microsoft curly quotes rather than the standard single-quote character to delimit the string. You need to use the standard character, not the Microsoft character (all your other strings appear to be using the standard character).
Taking a step back, it makes no sense to catch a no_data_found exception only to call dbms_output. There is no guarantee that data written to dbms_output will ever be sent to the client application or that the client application will ever display it to a user.
It also doesn't appear that you have any code that would potentially raise a no_data_found exception. Opening a cursor that returns 0 rows does not result in an exception. If you expected to receive exactly 1 row, you could write a SELECT INTO statement and that would raise an exception if anything other than 1 row was returned. If your goal here is to determine how many times you iterated through the loop, you could potentially use the %rowcount attribute of the cursor.
Finally, it would make thinks much clearer if you formatted your code so that lines were indented based on what block they were a part of. Code like
FOR rec IN cursor
LOOP
IF <<something>>
THEN
<<do something>>
END IF;
IF <<something else>>
THEN
<<something else>>
END IF;
<<more stuff>>
END LOOP;
is much easier to follow.

Related

display two DBMS_OUTPUT in a same line

1. DECLARE
2. temp1 NUMBER :=digit;
3. BEGIN
4. IF (digit<=0) THEN
5. Dbms_Output.Put_line( digit ||'! = ' || factorial);
6. ELSE
7. Dbms_Output.Put_line( digit ||'! = fact(' || digit||') = ');
8. END IF;
9. while (temp1 > 0)
10. loop
11. Dbms_Output.Put_line(temp1);
12. IF ( temp1!=1) THEN
13. Dbms_Output.Put_line(' * ');
14. ELSE Dbms_Output.Put_line(' ');
15. END IF;
16. temp1 := temp1 - 1;
17. end loop;
18. END;
I am trying to display line 13 in the same line of line 11 Is it possible
I have searched everywhere but no luck, every time i call dbms output it displays the out put in new line but I dont want that I want the output in same line. I want to try something similar to || pipe line so that i can display output in same line
Use dbms_output.put() for the first part:
loop
Dbms_Output.Put(temp1);
IF ( temp1!=1) THEN
Dbms_Output.Put_line(' * ');
ELSE
Dbms_Output.Put_line(' ');
END IF;
From the documentation:
PUT Procedure - Places a partial line in the buffer
PUT_LINE Procedure - Places line in buffer
fiddle

PL/SQL CREATE PROCEDURE - factorial

I don't know what's wrong with my code block to create a procedure to find out the factorial of a number. Thank you.
Question 1: write a stored procedure that gets an integer number n and calculates and displays its factorial.
SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE factorial_number(
n NUMBER) AS
factorial NUMBER;
num Number;
BEGIN
FOR i IN REVERSE 1..n LOOP
num := i - 1;
factorial := factorial * num;
END LOOP;
DBMS_OUTPUT.PUT_LINE (factorial);
EXCEPTION
WHEN OTHERS
THEN DBMS_OUTPUT.PUT_LINE ('Error!');
END;
/
BEGIN
factorial_number(5);
END;
/
You're failing to initialize the local variable factorial. Uninitialized variables are initially null and multiplying null by any value produces null.
I don't see why you'd want your loop to go in reverse order. It doesn't matter since multiplication is communitive but it it unlikely to make your code easier to read/ debug/ follow.
You don't want to subtract 1 from the value you are multiplying by on each iteration of the loop. When i = 1, for example, you're multiplying factorial by 0 which means that (assuming you initialize factorial), you'd always end up with 0. You want to multiply by i so there is no need for the local variable num.
If I make those fixes
CREATE OR REPLACE PROCEDURE factorial_number(
n NUMBER)
AS
factorial NUMBER := 1; -- Initialize
BEGIN
FOR i IN 1..n LOOP -- Loop normally
dbms_output.put_line( 'Beginning iteration ' || i || ' of loop. ' ||
'Factorial = ' || factorial ||
' multiplying by ' || i );
factorial := factorial * i; -- Multiply by i not i-1
END LOOP;
DBMS_OUTPUT.PUT_LINE (factorial);
EXCEPTION
WHEN OTHERS
THEN DBMS_OUTPUT.PUT_LINE ('Error!');
END;
Then
BEGIN
factorial_number(5);
END;
will print out 120 (5*4*3*2*1).
I'm also adding an additional dbms_output line to print out the current state of the variables on each iteration of the loop. That's a relatively old-school method of debugging. In the modern world, you'd walk through the code with a debugger where you can see the values of your local variables but introductory classes may not teach debugger usage initially.
How about
SQL> CREATE OR REPLACE PROCEDURE factorial_number (n NUMBER)
2 AS
3 factorial NUMBER := 1;
4 BEGIN
5 FOR i IN 1 .. n
6 LOOP
7 factorial := factorial * i;
8 END LOOP;
9
10 DBMS_OUTPUT.PUT_LINE (factorial);
11 END;
12 /
Procedure created.
SQL>
SQL> BEGIN
2 factorial_number (5);
3 END;
4 /
120
PL/SQL procedure successfully completed.
SQL>

Unable to get large data i.e. XML from oracle table using Procedure

I am tring to create update statement for a column which datatype is CLOB.
For this i am fetching XML from table and writing it in oracle console for later use.
For some of the data it is working fine.
For some i am getting error as
Exception1 : CREATE_UPDATE_XML_QUERY('600264','700009');
--ORA-06502: PL/SQL: numeric or value error
I have changed datatype of V_XML,V_BLOCK nothing has worked
PROCEDURE CREATE_UPDATE_XML_QUERY
(
MY_ID NUMBER,
MY_ID2 NUMBER
) AS
V_SCREEN_VERSION NUMBER;
V_XML_ID NUMBER;
V_CNT NUMBER;
V_XML CLOB);
V_BLOCK CLOB;
BEGIN
SELECT XML,XMLID INTO V_XML,V_XML_ID
FROM XML_TABLE WHERE ENC_ID = MY_ID AND SCREEN_ID = MY_ID2 ; ----getting excption
V_BLOCK :=
'
SET SERVEROUTPUT ON;
DECLARE
V_XML CLOB ;
BEGIN
';
V_BLOCK := V_BLOCK||'V_XML := '''||V_XML||''';';
V_BLOCK := V_BLOCK||'
UPDATE XML_TABLE SET XML = '||'V_XML'||'
WHERE ENC_ID = '||MY_ID||' AND ENC_TYPE = ''P'' AND SCREEN_ID = '||MY_ID2||' AND XMLID = '||V_XML_ID||';
--DBMS_OUTPUT.PUT_LINE(''V_XML =>''||V_XML);
DBMS_OUTPUT.PUT_LINE(''ROWCOUNT =>''||SQL%ROWCOUNT);
END;
/';
DBMS_OUTPUT.PUT_LINE('--Printing Annomous Block the XML :->>');
DBMS_OUTPUT.PUT_LINE(V_BLOCK);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Exception1 : UPDATE_SCREEN_MASTER_XML('''||MY_ID||''','''||MY_ID2||''','''||V_XML_ID||'''); --'||SQLERRM);--'||SQLERRM);
END CREATE_UPDATE_XML_QUERY;
How can i avoid error.
Is it because my XML is too big.
Well, I've come up with a test case to reproduce this (Oracle 12.2.0.1), and you're right, the problem isn't the DBMS_OUTPUT line.
declare
v_clob clob;
xmlid number;
begin
-- initialize clob and make clob a string of length 32768
dbms_lob.createtemporary(v_clob, true);
for i in 1..32768 loop
v_clob := v_clob || 'x';
end loop;
dbms_output.put_line(length(v_clob));
-- testing:
v_clob := v_clob || 'x'; -- appending a varchar2 works fine
v_clob := v_clob || xmlid; -- appending a number gives ORA-06502
v_clob := v_clob || 'x' || xmlid; -- appending a string+number still gives ORA-06502
v_clob := v_clob || to_clob(xmlid); -- works fine
dbms_lob.append(v_clob, 'x' || xmlid); -- also works fine
dbms_output.put_line(length(v_clob));
dbms_output.put_line(substr(v_clob,1,32767));
end;
/
The problem seems to be that when you concatenate strings with pipes, Oracle can append 2 clobs together if one of them is over 32k, and it can implicitly convert a varchar2 to a clob and append them. But if you try to append a number to a clob over 32k, it fails. It understands how to append varchar2 and number, and clob and clob, and clob and varchar2. But it can't seem to automatically figure out how to do number -> varchar2 -> clob. You can fix it by wrapping the string in to_clob(), avoiding the issue with Oracle's implicit conversion.
Where you get the error depends on the length of your table's XML CLOB value.
If the XML is more than 32k then you'll see the error at line 27 in your code, from trying to concatenate a varchar2 string a number (as #kfinity demonstrated) onto a CLOB; behaviour that isn't explained in the documentation, but is presumably something to do with implicit conversion since just explicitly converting the numbers with to_char(MY_ID) (or to_clob(MY_ID)).
If the XML is less than, but close to, 32k then you'll just scrape past that, but the V_BLOCK CLOB still ends up as greater than 32k, then will still error at line 39 as dbms_output can't handle that.
You can avoid the first problem by using to_char() around the numeric variables, or by using dbms_lob.append instead of concatenation:
...
V_BLOCK :=
'
SET SERVEROUTPUT ON;
DECLARE
V_XML CLOB ;
BEGIN
';
dbms_lob.append(V_BLOCK, 'V_XML := '''||V_XML||''';');
dbms_lob.append(V_BLOCK, '
UPDATE XML_TABLE SET XML = '||'V_XML'||'
WHERE ENC_ID = '||MY_ID||' AND ENC_TYPE = ''P'' AND SCREEN_ID = '||MY_ID2||' AND XMLID = '||V_XML_ID||';
--DBMS_OUTPUT.PUT_LINE(''V_XML =>''||V_XML);
DBMS_OUTPUT.PUT_LINE(''ROWCOUNT =>''||SQL%ROWCOUNT);
END;
/');
...
And you can avoid the second problem, as long as your XML value contains line breaks, by splitting the CLOB into lines, as shown here, but with a slight modification to handle empty lines; with additional variables declared as:
V_BUFFER VARCHAR2(32767);
V_AMOUNT PLS_INTEGER;
V_POS PLS_INTEGER := 1;
then instead of:
DBMS_OUTPUT.PUT_LINE(V_BLOCK);
you can do:
WHILE V_POS < length(V_BLOCK) LOOP
-- read to next newline if there is one, rest of CLOB if not
IF dbms_lob.instr(V_BLOCK, chr(10), V_POS) > 0 THEN
V_AMOUNT := dbms_lob.instr(V_BLOCK, chr(10), V_POS) - V_POS;
IF V_AMOUNT = 0 THEN
V_BUFFER := null; -- first character is a new line (i.e. a blank line)
ELSE
dbms_lob.read(V_BLOCK, V_AMOUNT, V_POS, V_BUFFER);
END IF;
V_POS := V_POS + V_AMOUNT + 1; -- skip newline character
ELSE
V_AMOUNT := 32767;
dbms_lob.read(V_BLOCK, V_AMOUNT, V_POS, V_BUFFER);
V_POS := V_POS + V_AMOUNT;
END IF;
DBMS_OUTPUT.PUT_LINE(V_BUFFER);
END LOOP;
db<>fiddle
#VinayakDwivedi edited to add a function to use instead:
PROCEDURE print_clob_to_output (p_clob IN CLOB)
IS
v_offset NUMBER := 1;
v_chunk_size NUMBER := 10000;
BEGIN
LOOP
EXIT WHEN v_offset > DBMS_LOB.getlength (p_clob);
DBMS_OUTPUT.put_line (
DBMS_LOB.SUBSTR (p_clob, v_chunk_size, v_offset));
v_offset := v_offset + v_chunk_size;
END LOOP;
END print_clob_to_output;
... but this will introduce extra line breaks every 10000 characters.
However, it's worth noting that the PL/SQL block you are generating within that ends up with a line like:
V_XML := '<original xml from table>';
and if that generated code is run it will also error if the original XML is more that 32k. Really that generated code also needs to be broken up to reconstruct your CLOB in chunks - i.e. a loop that takes 32k at a time and concatenates/appends those chunks to reconstitute the full value. It also has whitespace at the start of each line so the DECLARE etc. and more importantly the final / are not at the start of their respective lines, which will also cause problems when trying to run it as-is.
Check out:
https://www.techonthenet.com/oracle/errors/ora06502.php
This suggests that there may be 3 possible causes to this error
Number to big - I doubt if this is your issue, as the max numbers are pretty huge
Conversion error - you are trying to convert a non number to a number
Assigning NULL to a NOT NULL constrained variable - self explantory
without knowing more of the context it is not really possible to determine which of these is your issue.
Hope this helps!

Check if string contains certain characters

I'm trying to check if the login being inserted into employee table is given in a certain format, which is: the first letter is strictly given - x, then 5 letters and two numbers => xlogin00.
CREATE OR REPLACE TRIGGER check_login
BEFORE INSERT OR UPDATE OF login ON employee
FOR EACH ROW
DECLARE
xlogin00 employee.login%TYPE;
prefix VARCHAR2(1);
name VARCHAR2(5);
number VARCHAR2(2);
BEGIN
xlogin00 := :new.login;
prefix := substr(xlogin00,1,1);
name := substr(xlogin00,2,5);
number := substr(xlogin00,7,2);
if(LENGTH(xlogin00) != 8) then
Raise_Application_Error (-20203, 'Error');
end if;
if(prefix != 'x') then
Raise_Application_Error (-20204, 'Error');
end if;
if NOT REGEXP_LIKE(name, '[^a-z]') then
Raise_Application_Error (-20204, 'Error');
end if;
if NOT REGEXP_LIKE(number, '[^0-9]') then
Raise_Application_Error (-20204, 'Error');
end if;
END;
The only thing that seems to work in my code is if(prefix != 'x'). Other stuff doesn't work at all. What am I doing wrong?
You can do all of that with one check:
IF NOT REGEXP_LIKE(:new.login, '^x[a-z]{5}\d\d$') THEN
Raise_Application_Error (-20204, 'Error');
Note that you had also your boolean logic reversed: in both last tests you have a double negation (NOT in the IF and [^...] in the regex).
Note that in the proposed regex the ^ and $ are needed to make sure the match is with the complete login value, not just a substring.

ERROR: Reference the counter as the target of an assignment - PL/SQL

I'm relatively new to SQL.
I'm trying to print a simple pattern through this code
declare
n number(2):=5;
temp number(2):=n;
begin
for a in 1..5
a:=a+1;loop
for b in 1..temp loop
b:=b+1;
dbms_output.put_line(' ');
temp:=temp-1;
end loop;
for c in 1..2*a-1 loop
c:=c+1;
dbms_output.put_line('*');
end loop;
end loop;
end;
/
I keep getting this error:
PLS-00103: Encountered the symbol "A" when expecting one of the following:
* & - + / at loop mod remainder rem <an exponent (**)> ||
multiset
I understand Oracle doesn't allow to reference the counter as the target of an assignment which is why I keep getting error at line 6 but I'm unable to make it work even by declaring another global variable and assigning increment statement in it but it doesn't work either.
Please help.
Thanks!
Modifying the previous answers to actually give you Pascal's triangle, which you mentioned you were attempting in a comment:
set serveroutput on format wrapped
declare
n number(2):=5;
begin
for a in 1..n loop
for b in 1..n-a loop
dbms_output.put(' ');
end loop;
for c in 1..2*a-1 loop
dbms_output.put('*');
end loop;
dbms_output.new_line;
end loop;
end;
/
*
***
*****
*******
*********
PL/SQL procedure successfully completed.
Both your dbms_output.put_line calls needed to be just dbms_output.put, as that was printing each * on a line on its own. But you do need a line break after each time around the a loop, so I've added a dbms_output.newline at the end of that. You were also decrementing temp inside the b loop, which meant it was zero instead of (n-1) for the second time around the a loop; but you don't really need a separate temp variable at all as that is always the same as (n-a)+1 and the +1 just puts an extra space on every line. (I also made the a loop 1..n as I assume you want to change the value of n later in one place only). With n := 8:
*
***
*****
*******
*********
***********
*************
***************
Crucially though you also have to set serveroutput on format wrapped, otherwise the leading spaces you're generating in the b loop are discarded.
You can also do this in plain SQL, though you need to supply the 5 twice, or use a bind or substitution variable:
select lpad(' ', 5 - level, ' ') || rpad('*', (level * 2) - 1, '*') as pascal
from dual
connect by level <= 5
PASCAL
------------------------------
*
***
*****
*******
*********
Your b and c loops are just doing a manual lpad really.
When I took your code in my editor I first noticed, you tried to increase a before starting the loop, and Oracle gives first error at that point. And also it does not allow you to increase counter variable in for loop, (I don't know why) I checked on the internet and found that you can not set increment step for Oracle for loops also you can not set a value for counter variable in for loop.
The code below works fine for me :
declare
n number(2):=5;
temp number(2):=n;
begin
for a in 1..5
loop --a:=a+1;
for b in 1..temp loop
--b:=b+1;
dbms_output.put_line(' ');
temp:=temp-1;
end loop;
for c in 1..2*a-1 loop
--c:=c+1;
dbms_output.put_line('*');
end loop;
end loop;
end;
/
As StephaneM says, loop variables are incremented by the loop itself: you don't need to do a := a + 1, and most of all you CAN'T assign them ! Here is a corrected version:
declare
n number(2):=5;
temp number(2):=n;
begin
for a in 1..5
loop
for b in 1..temp loop
dbms_output.put_line(' ');
temp:=temp-1;
end loop;
for c in 1..2*a-1 loop
dbms_output.put_line('*');
end loop;
end loop;
end;
/