Having hard time figuring out the best attribute to use with cursor - sql

Using this attribute %NOTFOUND with this cursor is giving me issues. I also tried using %FOUND. Is There a better option? It keeps returning the Else even if the cursor comes back with something in it or not. I want it to send the first email if the cursor has something and the second one if the cursor has nothing.
CURSOR crs IS
select *
from user_objects
where status = 'INVALID';
BEGIN
OPEN crs;
Loop
FETCH crs bulk collect INTO messages limit 10;
EXIT WHEN message. count = '0';
End IF;
for indx in messages.FIRST .. messages.LAST
loop
email_body := email_body||CHR(10)||'OBJECT NAME: '||messages(indx).OBJECT_NAME||', OBJECT TYPE: '||messages(indx). OBJECT_TYPE||', STATUS: '||messages(indx).STATUS;
end loop;
END LOOP;
IF CRS%NOTFOUND THEN
Email_body := email_body||CHR(10)|| '' ||CHR(10)||'All of these objects are invalid in your database. Please troubleshoot the issue. Thank you.'
DBMS_OUTPUT_LINE(email_body||'Invalid obj.';
ELSE
Email_body := 'There are no invalid objects in your database. Thank You.';
DBMS_OUTPUT_LINE(email_body||'No Invalid obj.';
END IF;

You can try this for test (i think more readable and shorter):
declare
email_body varchar2( 4000 ) := '';
l_invalid_objects_list varchar2( 4000 ) := '';
begin
l_invalid_objects_list := '';
for i in ( select OBJECT_NAME, OBJECT_TYPE, STATUS from user_objects where status = 'INVALID' and rownum <= 10 ) --limited to 10 records
loop
l_invalid_objects_list := l_invalid_objects_list||CHR(10)||'OBJECT NAME: '||i.OBJECT_NAME||', OBJECT TYPE: '||i. OBJECT_TYPE||', STATUS: '||i.STATUS;
end loop;
if length( l_invalid_objects_list ) > 0 then
email_body := email_body||CHR(10)|| '' ||CHR(10)||l_invalid_objects_list||CHR(10)|| '' ||CHR(10)||'All of these objects are invalid in your database. Please troubleshoot the issue. Thank you.';
DBMS_OUTPUT.PUT_LINE(email_body||'Invalid obj.');
else
Email_body := 'There are no invalid objects in your database. Thank You.';
DBMS_OUTPUT.PUT_LINE(email_body||'No Invalid obj.');
end if;
end;

I'm assuming you want to use a bulk operation and that you want to do this in batches of 10 even though it might be overkill for the functionality.
So . . . you need to declare a table type based on the cursor (ROWTYPE) then declare a variable based on the type.
You want to add to the email_body for each object and when you've looped through the lot it looks like you want another message saying if there were any invalid objects.
Using %NOTFOUND is not right here as you exit your loop when the messages.COUNT = 0. When it is 0 the cursor will always be %NOTFOUND so you're always getting the INVALID option.
I've used a boolean that is set to TRUE as soon as you have at least one invalid object and that is checked instead of the %NOTFOUND.
DECLARE
CURSOR crs
IS
SELECT *
FROM user_objects
WHERE status = 'INVALID';
TYPE work_recs IS TABLE OF crs%ROWTYPE;
messages work_recs;
lbol_invalid_objects BOOLEAN := FALSE;
email_body VARCHAR2(2000);
BEGIN
OPEN crs;
LOOP
FETCH crs BULK COLLECT INTO messages LIMIT 10;
DBMS_OUTPUT.PUT_LINE('COUNT - '||messages.COUNT);
EXIT WHEN messages.COUNT = '0';
lbol_invalid_objects := TRUE;
FOR indx IN messages.FIRST .. messages.LAST
LOOP
email_body := email_body||CHR(10)||'OBJECT NAME: '||messages(indx).OBJECT_NAME||', OBJECT TYPE: '||messages(indx). OBJECT_TYPE||', STATUS: '||messages(indx).STATUS;
END LOOP;
END LOOP;
IF lbol_invalid_objects THEN
email_body := email_body||CHR(10)|| '' ||CHR(10)||'All of these objects are invalid in your database. Please troubleshoot the issue. Thank you.'
dbms_output.put_line('Invalid obj.');
ELSE
email_body := 'There are no invalid objects in your database. Thank You.';
dbms_output.put_line('No Invalid obj.');
END IF;
END;

Related

Function returning error texts in Oracle APEX

I am trying to take a count of the records in the interactive grid and based on that I am trying to pass a message to the user. However, I am getting error : ORA-06550: line 1, column 141: PLS-00103: Encountered the symbol "NUMBER" when expecting one of the following: := . ( # % ; The symbol "." was substituted for "NUMBER" to continue.Following is my code in the validation. Validation Type is : Function Returning Error Text.
l_count NUMBER := 0;
BEGIN
SELECT COUNT(1)
INTO l_count
FROM ugh
WHERE ugh.pre = :PRE
AND ugh.APP1 = :APP1
AND ugh.APP2 = :APP2
AND ugh.APP3 = :APP3
AND ugh.FINL_APP = :FINL_APP;
IF l_count > 1 THEN
IF END_DATE IS NULL THEN
RETURN 'Error Message to be displayed.';
ELSE
RETURN NULL;
END IF;
ELSE
RETURN NULL;
END IF;
END;
Can anyone please help ?
Looks like you're missing the DECLARE keyword:
DECLARE --> this
l_count NUMBER := 0;
BEGIN
SELECT COUNT (1)
INTO l_count
FROM ugh
Also, what is END_DATE? You never declared it. If it is a page item, then precede it with a colon, :END_DATE

PLS-00103: Encountered the symbol "=" when expecting one of the following.... in PL/SQL script

create or replace function lstnation (listdisplay in varchar2)
return varchar2 is
nName varchar2 (1000) default null;
listD varchar2(1000) default null;
cursor display_nation
is
select nation.n_name
from nation
inner join region
on region.r_regionkey = nation.n_nationkey
where region.r_regionname = listdisplay;
BEGIN
open display_nation;
loop
fetch display_nation into nName;
exit when display_nation%notfound;
IF
listD := listD || RTRIM(nName)||' , ';
end loop;
close display_nation;
return listD;
end lstnation;
/
DECLARE
rKey region.r_regionkey%type;
rName region.r_name%type;
nList varchar2(1000);
cursor outer_block is
select region.r_regionkey, region.r_name, lstnation(region.r_name)
from region;
BEGIN
open outer_block;
loop
fetch outer_block into rKey, rName, nList;
exit when outer_block%notfound;
dbms.output.put_line(rkey || ' ' || RTRIM(rName) || ': '|| nList);
end loop;
close outer_block;
end;
/
I get two errors, how can I fix it
LINE/COL ERROR
19/12 PLS-00103: Encountered the symbol "=" when expecting one of the
following:
. ( * # % & = - + < / > at in is mod remainder not rem then
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || multiset member submultiset
20/2 PLS-00103: Encountered the symbol "END" when expecting one of the
following:
begin function pragma procedure subtype type
current cursor delete
exists prior
You can save some coding and efficiency by replacing the cursor loop with the listagg function
select listagg(rtrim(nation.n_name),',')
from nation
inner join region
on region.r_regionkey = nation.n_nationkey
where region.r_regionname = listdisplay;
So that will collate all the matching rows, and use whatever delimiter is passed in. One thing to be aware of, you have listD varchar2(1000) so as long as the results from the query are less than 1000, you are OK. If you expect a larger result set, you may need to increase or use a clob.
If for some reason, you still want to use the loop method, then you need to fix your IF statement:
loop
fetch display_nation into nName;
exit when display_nation%notfound;
IF <condition> THEN
listD := listD || RTRIM(nName)||' , ';
END IF;
end loop;

PLS-00306: when calling a function from a PL/SQL block

I am getting the PLS-00306 error when I attempt to run a PL/SQL block that calls a variety of things including a function. The job of the function is to count how many cars belong to a certain model type. The function works if I call it in a SQL statement or it's own block, it just doesn't seem to work here.
This is the function:
CREATE OR REPLACE Function findtotalcarmodels(
model_name_in IN varchar2)
RETURN NUMBER
IS
counter NUMBER := 0;
CURSOR car_count_cur IS
SELECT model_name
FROM i_car
WHERE model_name = model_name_in;
Rec_car_details car_count_cur%ROWTYPE;
BEGIN
OPEN car_count_cur;
LOOP
FETCH car_count_cur INTO Rec_car_details;
EXIT WHEN car_count_cur%NOTFOUND;
counter := counter + 1;
END LOOP;
CLOSE car_count_cur;
RETURN counter;
END;
This is the Block:
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;
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;
END;
/
Before I get slammed for the style of the PL/SQL block, just keep in mind that it is written to requirement and everything works well with the exception of the function.
If someone could point me in the right direction to call this function without error I would be very grateful.
It looks like you have missed to pass the IN parameter to the function.
Try like this,
v_totalcars := findtotalcarmodels('<model_name_in>');
Well, when i see things right, you don't fillup the parameter model_name. When you don't have an overriden function wihtout parameter you need to fill it up.
AS you can also see the PLS-00306 is telling you something about wrong number of arguments.

Is there a way to restart a cursor? Oracle

I am trying to do something such as:
for(int i = 0; i<10; i++)
{
for(int j = 0; j<10; j++)
{
Blah;
}
}
//As you can see each time that there is a different i, j starts at 0 again.
Using cursors in Oracle. But if I'm correct, after I fetch all rows from a cursor, it will not restart. Is there a way to do this?
Here is my sql:
CREATE OR REPLACE PROCEDURE SSACHDEV.SyncTeleappWithClientinfo
as
teleCase NUMBER;
CURSOR TeleAppCursor
is
Select
distinct(casenbr)
from TeleApp;
CURSOR ClientInfoCursor
is
Select casenbr
from clientinfo
where trim(cashwithappyn) is null;
BEGIN
open TeleAppCursor;
open ClientInfoCursor;
LOOP
fetch TeleAppCursor into teleCase;
EXIT when TeleAppCursor%NOTFOUND;
LOOP
fetch ClientInfoCursor into clientCase;
EXIT when ClientInfoCursor%NOTFOUND;
if clientCase = teleCase then
update ClientInfo
set cashwithappyn = (select cashwithappyn from teleapp where casenbr = clientCase)
where casenbr = clientCase;
break;
end if;
END LOOP;
END LOOP;
END;
I did check online and was unable to find anything on this.
Instead of restarting the Cursor you could use a table variable to store the results of sql statement and then loop over the table an arbitrary number of times.
Here's an example using the SQL Fiddle Sample data.
DECLARE
CURSOR c1 IS
SELECT id,
TYPE,
details
FROM supportcontacts;
TYPE contactrec
IS TABLE OF c1%ROWTYPE INDEX BY BINARY_INTEGER;
acontact c1%ROWTYPE;
contactlist CONTACTREC;
counter INTEGER;
BEGIN
counter := 0;
OPEN c1;
LOOP
FETCH c1 INTO acontact;
IF c1%FOUND THEN
counter := counter + 1;
END IF;
Contactlist(counter) := acontact;
IF c1%NOTFOUND THEN
EXIT;
END IF;
END LOOP;
CLOSE c1;
FOR i IN 1..5 LOOP
FOR j IN 1..counter LOOP
dbms_output.Put_line(Contactlist(j).type || ' ' || Contactlist(j).details);
END LOOP;
END LOOP;
END;
/
which outputs
Email admin#sqlfiddle.com
Twitter #sqlfiddle
Email admin#sqlfiddle.com
Twitter #sqlfiddle
Email admin#sqlfiddle.com
Twitter #sqlfiddle
Email admin#sqlfiddle.com
Twitter #sqlfiddle
Email admin#sqlfiddle.com
Twitter #sqlfiddle
Here's the SQL Fiddle but I can't figure out how to see the output from dbms_output
You don't need the second cursor at all, just use the set operations in Oracle to update the appropriate records without manually searching for them yourself:
DECLARE
v_teleCase TeleApp.teleCase%TYPE;
v_cashwithappyn TeleApp.cashwithappyn%TYPE
CURSOR TeleAppCursor
is
Select
distinct casenbr, cashwithappyn
from TeleApp;
BEGIN
open TeleAppCursor;
LOOP
fetch TeleAppCursor into v_teleCase, v_cashwithappyn;
EXIT when TeleAppCursor%NOTFOUND;
UPDATE ClientInfo
SET cashwithappyn = v_cashwithappyn
WHERE casenbr = v_teleCase
AND trim(cashwithappyn) is null;
END LOOP;
END;
It's also a good idea to not have variables with the same name as columns.

How fix double encoding in PostgreSQL?

I have a table in PostgreSQL with words, but some words have invalid UTF-8 chars like 0xe7e36f and 0xefbfbd.
How I can identify all chars inside words that are invalid and replace they with some symbol like ??
EDIT: My database is in UTF-8, but I think there are double encoding from various other encodings. I think this because when I tried to convert to one type as LATIN1, I get an error saying that some char don't exists in that encoding, when I change to LATIN2 I get
the same error, but with another character.
So, what is possible to do to solve this?
Usage
It's a solution for my specific case, but maybe with some modifications can help another people.
Usage
SELECT fix_wrong_encoding('LATIN1');
Function
-- Convert words with wrong encoding
CREATE OR REPLACE FUNCTION fix_wrong_encoding(encoding_name VARCHAR)
RETURNS VOID
AS $$
DECLARE
r RECORD;
counter INTEGER;
token_id INTEGER;
BEGIN
counter = 0;
FOR r IN SELECT t.id, t.text FROM token t
LOOP
BEGIN
RAISE NOTICE 'Converting %', r.text;
r.text := convert_from(convert_to(r.text,encoding_name),'UTF8');
RAISE NOTICE 'Converted to %', r.text;
RAISE NOTICE 'Checking existence.';
SELECT id INTO token_id FROM token WHERE text = r.text;
IF (token_id IS NOT NULL) THEN
BEGIN
RAISE NOTICE 'Token already exists. Updating ids in textblockhastoken';
IF(token_id = r.id) THEN
RAISE NOTICE 'Token is the same.';
CONTINUE;
END IF;
UPDATE textblockhastoken SET tokenid = token_id
WHERE tokenid = r.id;
RAISE NOTICE 'Removing current token.';
DELETE FROM token WHERE id = r.id;
END;
ELSE
BEGIN
RAISE NOTICE 'Token don''t exists. Updating text in token';
UPDATE token SET text = r.text WHERE id = r.id;
END;
END IF;
EXCEPTION WHEN untranslatable_character THEN
--do nothing
WHEN character_not_in_repertoire THEN
--do nothing
END;
counter = counter + 1;
RAISE NOTICE '% token converted', counter;
END LOOP;
END
$$
LANGUAGE plpgsql;