PLSQL Check if varchar2 contains True - sql

I wrote a PLSQL Script which should check if one of the selected rows contains TRUE if yes then he should print me a text.. but he fetches more than one row.
DECLARE
tmp varchar2(20);
BEGIN
Select Status into tmp
From Cdb_Dv_Status;
IF tmp = 'TRUE' THEN
DBMS_Output.put_line('the output is false');
ELSE
DBMS_Output.put_line('the output is true');
END IF;
end;
/
This is what i get when i select it with SQL
NAME STATUS
------------------- -----------
DV_ENABLE_STATUS FALSE
DV_APP_PROTECTION NOT CONFIGURED
DV_CONFIGURE_STATUS FALSE
DV_APP_PROTECTION NOT CONFIGURED
DV_ENABLE_STATUS FALSE
DV_CONFIGURE_STATUS FALSE
DV_APP_PROTECTION NOT CONFIGURED
DV_CONFIGURE_STATUS FALSE
DV_ENABLE_STATUS FALSE
DV_CONFIGURE_STATUS FALSE
DV_APP_PROTECTION NOT CONFIGURED
DV_ENABLE_STATUS FALSE
this doesnt work either.. he tells me i have to declare tmp
BEGIN
for rec in (
select status tmp
from Cdb_Dv_Status
)loop
IF rec.tmp='TRUE' then
dbms_output.put_line('Database Vault wird genutzt');
end if;
end loop;
IF rec.tmp='TRUE' then
dbms_output.put_line('wird genutzt');
ELSE
dbms_output.put_line('Database Vault wird nicht genutzt');
END IF;
end;
/

You may get some too_many_rows or no_data_found exceptions depending on the data for the current case, rather prefer using a COUNT aggregation as in the following code block to check the existence
DECLARE
tmp INT;
BEGIN
SELECT COUNT(*)
INTO tmp
FROM Cdb_Dv_Status
WHERE Status = 'TRUE';
IF tmp > 0 THEN
DBMS_Output.put_line('the output is true');
ELSE
DBMS_Output.put_line('the output is false');
END IF;
END;
/
Demo

If all you need to do is to check if the value 'TRUE' appears at least once in the column STATUS of table (or view) CDB_DV_STATUS, you can do something like this:
declare
str varchar2(5);
begin
select case when 'TRUE' in (select status from cdb_dv_status)
then 'true' else 'false' end
into str
from dual;
dbms_output.put_line('The output is ' || str);
end;
/

Related

PL/SQL: Cursor doubles the last record from the table

I created the following PL/SQL anonymous block. The cursor below retrieves data from the select statement:
select mod_benutzer, count(*)
from dok_auspraegung
where parent_objekt_id = 1093
group by mod_benutzer;
This statement displays exactly two records:
DDMS_USER | 8
HU2MAMU | 14
But when I want to display these two records by cursor, it displays "HU2MAMU|14" two times like below:
Modifications:
DDMS_USER, 8x
HU2MAMU, 14x
HU2MAMU, 14x
declare
my_exception_1 exception;
var_parent_objekt_id dok_auspraegung.parent_objekt_id%TYPE := 1093;
var_date varchar(30);
var_mod_benutzer varchar2(10);
var_benutzer_modifs number;
cursor cursor_dok_auspraegung
is select mod_benutzer, count(*) from dok_auspraegung
where parent_objekt_id = 10935797565
group by mod_benutzer;
begin
select distinct to_char(mod_datum,'YYYY-MON-DD') into var_date from dok_auspraegung where parent_objekt_id = var_parent_objekt_id;
IF var_date is not null THEN
dbms_output.put_line('Parent Object ID' || ': ' || var_parent_objekt_id);
dbms_output.put_line('Date: ' || ' ' || var_date);
ELSE RAISE my_exception_1;
END IF;
open cursor_dok_auspraegung;
dbms_output.put_line('Modifications:');
loop
fetch cursor_dok_auspraegung into var_mod_benutzer, var_benutzer_modifs;
dbms_output.put(var_mod_benutzer);
dbms_output.put_line(', ' || var_benutzer_modifs || 'x');
exit when cursor_dok_auspraegung%notfound;
end loop;
dbms_output.put_line(cursor_dok_auspraegung%rowcount);
close cursor_dok_auspraegung;
exception
when NO_DATA_FOUND then
dbms_output.put_line('Parent Object ID not found!');
when my_exception_1 then
dbms_output.put_line('');
end;
What is the reason of that?
Because exiting from the cursor occurs after printing the value of the variables in the current case, this repeats the last value to be printed. So, it should occur before printing as follows
loop
fetch cursor_dok_auspraegung into var_mod_benutzer, var_benutzer_modifs;
exit when cursor_dok_auspraegung%notfound;
dbms_output.put(var_mod_benutzer);
dbms_output.put_line(', ' || var_benutzer_modifs || 'x');
end loop;

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

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;

While Loop and If/Else in PL/SQL

I am trying to write a procedure that will produce the following output
exec WinOrLose(4)
Welcome to the Win or Lose Game. Your number is 4.
You win.
You lose.
You win.
You lose.
==> You lose!
So far I have this:
CREATE or REPLACE Procedure WinOrLose (
p_choice number ) AS
v_answer number;
DECLARE
v_answer := p_choice
BEGIN
dbms_output.put_line ('Welcome to the Win or Lose Game. Your number is ' ||
v_answer);
FOR v_answer in 1..10
IF MOD(v_answer, 2) = 0 THEN -- v_answer is even
dbms_output.put_line (You lose)
END;
/
I'm unsure of where to go from there. My thought process (psuedocode) is this:
SET v_answer := 1
While Loop (outside)
MOD(v_answer,2) = 0 then dbms.output (YOU LOSE)
ELSE
dbms.output (YOU WIN)
end if;
v_answer := p_choice
CREATE or REPLACE Procedure WinOrLose (
p_choice number ) AS
BEGIN
dbms_output.put_line ('Welcome to the Win or Lose Game. Your number is ' ||
p_choice);
FOR v_counter in 1..p_choice LOOP
IF (MOD(v_counter, 2) = 0)
THEN
dbms_output.put_line ('You win');
ELSE
dbms_output.put_line ('You lose');
END IF;
END LOOP;
IF (MOD(p_choice , 2) = 0)
THEN
dbms_output.put_line ('==> You win!');
ELSE
dbms_output.put_line ('==> You lose!');
END IF;
END;
/

PL/SQL Function returning wrong result

I've a PL/SQL function below that's returning a wrong result in SQL Navigator and SQL Developer, but is returning the right answer in SQL Plus.
We've a script running that's executing it and returning the wrong answer too, so trying to fix it. Can anyone see any issues with it? It works fine for most people, but I've a few people going into and returning null/nothing in SQL Navigator and Developer. It's not populating l_end_date for them, and thus credits not populating.
Works fine then in SQL Plus for some reason.
create or replace function mis_get_mem_lcr_credits(p_mem_no in number) RETURN number is
--
v_lcr_credit number;
l_mem_no number;
l_start_date date;
l_end_date date;
l_dob date;
l_18th_date date;
--
cursor c1 is
select mem_no, ind_birth_dt
from cd_individual
where mem_no = l_mem_no
and pkg_mem_utils.get_member_age(mem_no,ind_birth_dt) >= 18
and nvl(ind_student_flag,'N') = 'N'
order by mem_no, ind_birth_dt;
--
cursor c2 is
select distinct m_effdt,
m_termdt
from cd$v_member_contracts9 cd1,
cd_member_product_link cd2
where cd1.mem_no = l_mem_no
and cd1.policy_no = cd2.policy_no
and cd1.m_effdt = cd2.mem_product_eff_dt --.2
and (l_18th_date between cd1.m_effdt and cd1.m_termdt OR cd1.m_effdt > l_18th_date)--.3 18 at time of contract effective date
and nvl(cd1.lapsed_to_start,'N') = 'N'
and cd2.product_id not in (14,41,31) -- Exclude No Cover, DentalProtect and HealthProtect
and cd2.product_id NOT IN (select distinct product_id
from cd_product_options
where nvl(allowed_for_lcr,'Y') = 'N')
order by cd1.m_effdt ASC;
--
begin
--
l_mem_no := p_mem_no;
v_lcr_credit := 0;
l_dob := null;
--
for crec in c1 loop
--
l_dob := crec.ind_birth_dt;
--
-- l_18th_date := substr(to_char(l_dob,'DD/MM/YYYY'),0,6)||(substr(to_char(l_dob,'DD/MM/YYYY'),7,4)+18);
if to_char(l_dob) like '29-02%' then
l_18th_date := add_months(to_date(l_dob+1),216 );
else
l_18th_date := add_months(to_date(l_dob), 216);
end if;
--
for crec2 in c2 loop
--
if crec2.m_termdt > sysdate then
--
l_end_date := sysdate;
--
else
--
l_end_date := crec2.m_termdt;
--
end if;
--
if v_lcr_credit = 0 then --earliest contract
--
if l_18th_date between crec2.m_effdt and crec2.m_termdt then
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,l_18th_date);
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
end loop;
--
end loop;
--
return round(nvl(v_lcr_credit,0));
--
end mis_get_mem_lcr_credits;
/
show errors
spool off
exit
Never, ever use to_date() on a DATE value.
to_date() converts a varchar to a date.
If you call it with a DATE the date value gets converted to a varchar which then gets converted back to a date which it was to begin with - and being subject to the evil implicit data type conversion twice in that process.
The variable l_dob is defined as DATE so you have to change
add_months(to_date(l_dob+1),216 );
...
add_months(to_date(l_dob), 216);
to
add_months(l_dob+1,216);
...
add_months(l_dob, 216);
Could be because of different values of
NLS_TERRITORY, NLS_DATE_FORMAT etc. in different environments.
So I would suggest to set explicitly these values in your script. e.g. something like EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TERRITORY=''AMERICA''';
Some References:
NLS_DATE_FORMAT
NLS_TERRITORY

Oracle : how to fetch data from dynamic query?

I have a program to generate dynamic query string based on input. This query may select from any tables or joined tables in my DB, and the column names and number of columns are unknown.
Now with this query string as the only input, I want to fetch all data from the result and output them line by line, is there any way to do this ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Thank Thinkjet for the reference. I have solved the problem, to help the others, here is the piece of code I used:
DECLARE
v_curid NUMBER;
v_desctab DBMS_SQL.DESC_TAB;
v_colcnt NUMBER;
v_name_var VARCHAR2(10000);
v_num_var NUMBER;
v_date_var DATE;
v_row_num NUMBER;
p_sql_stmt VARCHAR2(1000);
BEGIN
v_curid := DBMS_SQL.OPEN_CURSOR;
p_sql_stmt :='SELECT * FROM emp';
DBMS_SQL.PARSE(v_curid, p_sql_stmt, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS(v_curid, v_colcnt, v_desctab);
-- Define columns:
FOR i IN 1 .. v_colcnt LOOP
IF v_desctab(i).col_type = 2 THEN
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_num_var);
ELSIF v_desctab(i).col_type = 12 THEN
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_date_var);
ELSE
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_name_var, 50);
END IF;
END LOOP;
v_row_num := dbms_sql.execute(v_curid);
-- Fetch rows with DBMS_SQL package:
WHILE DBMS_SQL.FETCH_ROWS(v_curid) > 0 LOOP
FOR i IN 1 .. v_colcnt LOOP
IF (v_desctab(i).col_type = 1) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_name_var);
ELSIF (v_desctab(i).col_type = 2) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_num_var);
ELSIF (v_desctab(i).col_type = 12) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_date_var);
END IF;
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(v_curid);
END;
/
You can do that with DBMS_SQL package.
Update
To get more detailed reference about DBMS_SQL go here.
If you are building your string within PL/SQL, you can run it with EXECUTE IMMEDIATE. <- link. Use the BULK COLLECT INTO and output the collection.
<PRE>
DECLARE
RUN_S CLOB;
IGNORE NUMBER;
SOURCE_CURSOR NUMBER;
PWFIELD_COUNT NUMBER DEFAULT 0;
L_DESCTBL DBMS_SQL.DESC_TAB2;
Z_NUMBER NUMBER;
BEGIN
RUN_S := ' SELECT 1 AS VAL1,
2 AS VAL2,
CURSOR (SELECT 11 AS VAL11,
12 AS VAL12
FROM DUAL) AS CUR1,
CURSOR (SELECT 11 AS VAL11,
12 AS VAL12
FROM DUAL) AS CUR2
FROM DUAL';
SOURCE_CURSOR := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(SOURCE_CURSOR, RUN_S, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(SOURCE_CURSOR, PWFIELD_COUNT, L_DESCTBL); -- get record structure
FOR I IN 1 .. PWFIELD_COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Col ' || I || ' Type:' || L_DESCTBL(I).COL_TYPE);
IF L_DESCTBL(I).COL_TYPE = 2 THEN
DBMS_SQL.DEFINE_COLUMN(SOURCE_CURSOR, I, Z_NUMBER);
END IF;
NULL;
END LOOP;
IGNORE := DBMS_SQL.EXECUTE(SOURCE_CURSOR);
LOOP
IF DBMS_SQL.FETCH_ROWS(SOURCE_CURSOR) > 0 THEN
FOR I IN 1 .. PWFIELD_COUNT LOOP
IF L_DESCTBL(I).COL_TYPE IN (2) THEN
DBMS_SQL.COLUMN_VALUE(SOURCE_CURSOR, I, Z_NUMBER);
DBMS_OUTPUT.PUT_LINE('Col ' || I || ' Value:' || Z_NUMBER);
END IF;
END LOOP;
ELSE
EXIT;
END IF;
END LOOP;
END;
</PRE>