IF EXISTS condition not working with PLSQL - sql

I am trying to print the TEXT when condition is TRUE. The select code is perfectly working fine. It's showing 403 value when i only run select code. But I have to print some text when condition exists. What's the problem with following code.
BEGIN
IF EXISTS(
SELECT CE.S_REGNO FROM
COURSEOFFERING CO
JOIN CO_ENROLMENT CE
ON CE.CO_ID = CO.CO_ID
WHERE CE.S_REGNO=403 AND CE.COE_COMPLETIONSTATUS = 'C' AND CO.C_ID = 803
)
THEN
DBMS_OUTPUT.put_line('YES YOU CAN');
END;
Here is the error report:
Error report:
ORA-06550: line 5, column 1:
PLS-00103: Encountered the symbol "JOIN" when expecting one of the following:
) , with group having intersect minus start union where
connect
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:

IF EXISTS() is semantically incorrect. EXISTS condition can be used only inside a SQL statement. So you might rewrite your pl/sql block as follows:
declare
l_exst number(1);
begin
select case
when exists(select ce.s_regno
from courseoffering co
join co_enrolment ce
on ce.co_id = co.co_id
where ce.s_regno=403
and ce.coe_completionstatus = 'C'
and ce.c_id = 803
and rownum = 1
)
then 1
else 0
end into l_exst
from dual;
if l_exst = 1
then
DBMS_OUTPUT.put_line('YES YOU CAN');
else
DBMS_OUTPUT.put_line('YOU CANNOT');
end if;
end;
Or you can simply use count function do determine the number of rows returned by the query, and rownum=1 predicate - you only need to know if a record exists:
declare
l_exst number;
begin
select count(*)
into l_exst
from courseoffering co
join co_enrolment ce
on ce.co_id = co.co_id
where ce.s_regno=403
and ce.coe_completionstatus = 'C'
and ce.c_id = 803
and rownum = 1;
if l_exst = 0
then
DBMS_OUTPUT.put_line('YOU CANNOT');
else
DBMS_OUTPUT.put_line('YES YOU CAN');
end if;
end;

Unfortunately PL/SQL doesn't have IF EXISTS operator like SQL Server. But you can do something like this:
begin
for x in ( select count(*) cnt
from dual
where exists (
select 1 from courseoffering co
join co_enrolment ce on ce.co_id = co.co_id
where ce.s_regno = 403
and ce.coe_completionstatus = 'C'
and co.c_id = 803 ) )
loop
if ( x.cnt = 1 )
then
dbms_output.put_line('exists');
else
dbms_output.put_line('does not exist');
end if;
end loop;
end;
/

Related

trying to check record exist in table -Oracle sql [duplicate]

I need to check a condition. i.e:
if (condition)> 0 then
update table
else do not update
end if
Do I need to store the result into a variable using select into?
e.g:
declare valucount integer
begin
select count(column) into valuecount from table
end
if valuecount > o then
update table
else do
not update
You cannot directly use a SQL statement in a PL/SQL expression:
SQL> begin
2 if (select count(*) from dual) >= 1 then
3 null;
4 end if;
5 end;
6 /
if (select count(*) from dual) >= 1 then
*
ERROR at line 2:
ORA-06550: line 2, column 6:
PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:
...
...
You must use a variable instead:
SQL> set serveroutput on
SQL>
SQL> declare
2 v_count number;
3 begin
4 select count(*) into v_count from dual;
5
6 if v_count >= 1 then
7 dbms_output.put_line('Pass');
8 end if;
9 end;
10 /
Pass
PL/SQL procedure successfully completed.
Of course, you may be able to do the whole thing in SQL:
update my_table
set x = y
where (select count(*) from other_table) >= 1;
It's difficult to prove that something is not possible. Other than the simple test case above, you can look at the syntax diagram for the IF statement; you won't see a SELECT statement in any of the branches.
Edit:
The oracle tag was not on the question when this answer was offered, and apparently it doesn't work with oracle, but it does work with at least postgres and mysql
No, just use the value directly:
begin
if (select count(*) from table) > 0 then
update table
end if;
end;
Note there is no need for an "else".
Edited
You can simply do it all within the update statement (ie no if construct):
update table
set ...
where ...
and exists (select 'x' from table where ...)
not so elegant but you dont need to declare any variable:
for k in (select max(1) from table where 1 = 1) loop
update x where column = value;
end loop;

Oracle optimize select after update status performance

I have a store procedure that will
Update maximum 500 rows status from 0 to 1
Return those rows to program via cursor
Here is my store procedure code
PROCEDURE process_data_out (
o_rt_cursor OUT SYS_REFCURSOR
) IS
v_limit NUMBER;
l_data_ids VARCHAR2(32000);
BEGIN
v_limit := 500; -- limit 500
l_data_ids := '';
-- Create loop to get data
FOR i IN (
SELECT *
FROM
(
SELECT id FROM
TBL_DATA a
WHERE
a.created_at BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND a.status = 0
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
ORDER BY
priority
)
WHERE
ROWNUM <= v_limit
) LOOP
BEGIN
-- Build string of ids like id1,id2,id3,
l_data_ids := l_data_ids
|| i.id
|| ',';
-- update row status to prevent future repeat
UPDATE TBL_DATA
SET
status = 1
WHERE
id = i.id;
END;
END LOOP;
COMMIT;
-- If string of ids length >0 open cursor to take data
IF ( length(l_data_ids) > 0 )
THEN
-- Cut last comma id1,id2,id3, --> id1,id2,id3
l_data_ids := substr(l_data_ids,1,length(l_data_ids) - 1);
-- open cursor
OPEN o_rt_cursor FOR
SELECT
id,
phone
FROM
TBL_DATA a
WHERE
a.id IN (
SELECT
to_number(column_value)
FROM
XMLTABLE ( l_data_ids )
);
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END process_data_out;
I want to optimize this performance and here is my question
Should I replace in by exists
Replace
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
by
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_1 where TBL_BIG_TABLE_1.phone = a.phone)
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_2 WHERE TBL_BIG_TABLE_2.phone = a.phone and IS_DENY = 1)
Is there a better way than
Save a string of ids like id1,id2,id3 after update row status
Open cursor by select from string of ids
I appreciate for any suggestion.
Thank for your concern
Row-by-row processing is always slower and you are also creating the string of ids, which again takes time so overall performance is going down.
You can use the collection DBMS_SQL.NUMBER_TABLE to store the updated ids from the UPDATE statement using the RETURNING clause and use it in the cursor query.
Also, I have changed your update statement so that it does not use NOT IN and uses the LEFT JOINS and ROW_NUMBER analytical function for increasing the performance as follows:
CREATE OR REPLACE PROCEDURE PROCESS_DATA_OUT (
O_RT_CURSOR OUT SYS_REFCURSOR
) IS
V_LIMIT NUMBER;
L_DATA_IDS DBMS_SQL.NUMBER_TABLE;
BEGIN
V_LIMIT := 500; -- limit 500
UPDATE TBL_DATA A
SET A.STATUS = 1
WHERE A.ID IN (
SELECT ID
FROM ( SELECT ID,
ROW_NUMBER() OVER(ORDER BY PRIORITY) AS RN
FROM TBL_DATA B
LEFT JOIN TBL_BIG_TABLE_1 T1 ON T1.PHONE = B.PHONE
LEFT JOIN TBL_BIG_TABLE_2 T2 ON T2.IS_DENY = 1 AND T2.PHONE = B.PHONE
WHERE B.CREATED_AT BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND B.STATUS = 0
AND T1.PHONE IS NULL
AND T2.PHONE IS NULL)
WHERE RN <= V_LIMIT ) RETURNING ID BULK COLLECT INTO L_DATA_IDS;
OPEN O_RT_CURSOR FOR SELECT ID, PHONE
FROM TBL_DATA A
WHERE A.ID IN (SELECT COLUMN_VALUE FROM TABLE ( L_DATA_IDS ));
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END PROCESS_DATA_OUT;
/

Bulk Collect with Sum function

I am trying to use Bulk all and Forall in Oracle database:
Original code from Procedure is as below:
IF NVL(v_mc,0) != 0 THEN
FOR rec IN
(SELECT a.testid,
SUM(pct * NVL(cap,0))/v_mc lead1
BULK COLLECT INTO testids1, testids2
FROM testtable a
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid;
)
LOOP
UPDATE testtable
SET LEAD1 =ROUND(testids2(i),2)
WHERE tid = n_id
AND type = n_type
AND testid =testids1(i);
END LOOP;
END IF;
So In select statement , I am using Sum function and also using aliasing here .
Code , I have written which use Bulk collect and Forall is as follows:
PROCEDURE test
IS
TYPE test1Tab IS TABLE OF sh_rpt_temp_peer_wip.test1%TYPE;
TYPE test2Tab IS TABLE OF testtable.lead1%TYPE;
testids1 testidTab; --Error 1 and Error 2
testids2 LeadTab;
BEGIN
IF NVL(v_mc,0) != 0 THEN
SELECT testid,
SUM(pct * NVL(cap,0))/v_mc lead1
BULK COLLECT INTO testids1, testids2
FROM testtable a --Error 3
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid ORDER BY a.testid;
FORALL i IN testids1.FIRST..testids1.LAST
UPDATE testtable
SET LEAD1 =ROUND(testids2(i),2)
WHERE tid = n_id --Error 3
AND type = n_type
AND testid =testids1(i);
END IF;
END;
But while I am compiling procedure , I am getting multiple errors. I am very new to PL/SQL. Please let me know if I can retrieve calculated value as a Column in Bulk Collect?
I am getting below errors in procedure:
Error 1) PL/SQL: Item ignored
Error 2) component 'LEAD' must be declared
Error 3) expression is of wrong type
Please let me know what is wrong here
Thanks
As I identified that the collection type that you are referring is not in scope in the procedure, might be you have declared globally. I modified your code get a try it once, hopefully, it works for you.
PROCEDURE test
IS
TYPE test1Tab IS TABLE OF testtable.testid%TYPE;
TYPE test2Tab IS TABLE OF number;
testids1 test1Tab; //Error 1 and Error 2
testids2 test2Tab;
BEGIN
IF NVL(v_mc,0) != 0 THEN
SELECT testid,
SUM(pct * NVL(cap,0))/v_mc lead
BULK COLLECT INTO testids1, testids2
FROM testtable a //Error 3
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid ORDER BY a.testid;
FORALL i IN testids1.FIRST..testids1.LAST
UPDATE testtable
SET LEAD = ROUND(testids2(i),2)
WHERE tid = n_id //Error 3
AND type = n_type
AND testid = testids1(i);
END IF;
END;

SQL Update with CASE

could you please help me with this SQL?
UPDATE VERSION
SET VERSION_STATUS_ID = IF((SELECT COUNT(*) from VERSION where KEY = 'ABC') > 1, 5, 6)
WHERE VERSION_ID = 1;
Error report -
SQL Error: ORA-00907: missing right parenthesis
00907. 00000 - "missing right parenthesis"
Thanks
Use a CASE expression:
UPDATE VERSION
SET VERSION_STATUS_ID = CASE WHEN (SELECT COUNT(*) from VERSION where KEY = 'ABC') > 1
THEN 5 ELSE 6 END
WHERE VERSION_ID = 1;
EXISTS is usually more efficient than COUNT(*) in a subquery. If you have a unique id column in the table, you can use:
UPDATE VERSION v
SET VERSION_STATUS_ID = (CASE WHEN EXISTS (SELECT 1
FROM VERSION v2
WHERE v2.KEY = 'ABC' AND
v2.VERSION_ID <> v.VERSION_ID
) > 1
THEN 5 ELSE 6
END)
WHERE VERSION_ID = 1;
This assumes that VERSION_ID is unique. If you don't have such an explicitly declared column, you can use ROWID instead.
try this is working very good
SET SERVEROUTPUT ON;
DECLARE
EXPE NUMBER;
SAL NUMBER;
BEGIN
SELECT TRUNC((SYSDATE - hire_date) / 365) INTO EXPE
FROM EMPLOYEES
WHERE EMPLOYEE_ID = 155;
UPDATE EMPLOYEES
SET SALARY = CASE
WHEN EXPE > 15 THEN SALARY+(SALARY*20/100)
WHEN EXPE < 15 THEN SALARY+(SALARY*30/100)
ELSE 0
END;
SELECT SALARY INTO SAL
FROM EMPLOYEES
WHERE EMPLOYEE_ID =155;
DBMS_OUTPUT.PUT_LINE('THIS IS THE SALARY ' || ' ' ||SAL || ' ' || ' AND THIS IS THE EXPERINCE '||EXPE);
END;

01403. 00000 - "no data found"

I'm updating my docmeta table but i get the
ORA-01403: no data found
ORA-06512: at line 25
01403. 00000 - "no data found" error
Below is the query. How can i get rid of this error?
DECLARE
varPayTerm varchar2(300);
BEGIN
FOR X IN(
SELECT
d.did, d.xproject_id
FROM
revisions r,
(SELECT DDOCNAME, MAX(DID) mDID
FROM
REVISIONS REV
WHERE
dcreatedate >='01-Jan-14'
GROUP BY
DDOCNAME
) RevLatestID,
docmeta d
--temp_project p
WHERE
RevLatestID.mdid = r.did
and d.did = r.did
)
loop
select paymentterm
into varPayTerm
from project where pid = X.xproject_id and paymentterm is not null;
update docmeta
set xpaymentterm= varPayTerm
where did=X.dID
and xproject_id = X.xproject_id;
END LOOP;
END ;
The SELECT...INTO statements is returning no rows
If it is valid for no rows to be returned, and you want to continue you can catch and ignore the NO_DATA_FOUND exception like this:
...
BEGIN
SELECT ... INTO...;
EXCEPTION
WHEN NO_DATA_FOUND THEN
paymentterm := 0;
-- or ant msg you want
then return 'no data here';
END;
...