PLSQL: using subquery in if-statement error PLS-00405 - sql

I'm getting error pls-00405 when I try to run this code:
BEGIN
IF :P10_KAART_CODE IN (SELECT KAART_CODE FROM CADEAUKAART) THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;
There are some similar questions about this but couldn't find a solution for this simple code. Is there another way of writing this without facing a error?

PL/SQL doesn't support embedded SQL in if statements. So you'll need to rewrite your code like this:
create or replace function validate_kaart_code
(P10_KAART_CODE in CADEAUKAART.KAART_CODE%type)
return boolean
is
rv boolean;
l_code CADEAUKAART.KAART_CODE%type;
BEGIN
begin
SELECT KAART_CODE into l_code
FROM CADEAUKAART
where KAART_CODE =:P10_KAART_CODE
and rownum = 1 -- only necessary if KAART_CODE is not unique
;
rv := TRUE;
exception
when no_data_found then
rv := FALSE;
end;
RETURN rv;
END;
I have attempted to reconstruct your whole functionality from the snippet you posted. If this isn't what you intended and you can't convert it to fit your needs please provide more details.

I'd move the condition to the query itself, and catch a NO_DATA_FOUND exception:
BEGIN
SELECT * FROM CADEAUKAART WHERE kaart_code = :P10_KAART_CODE;
RETURN TRUE;
EXCEPTION WHEN NO_DATA_FOUND THEN
RETURN FALSE;
END;

One more way:
CREATE OR REPLACE FUNCTION IS_KAART_CODE_VALID(pinKAART_CODE IN CADEAUKAART.KAART_CODE%TYPE)
RETURN BOOLEAN
IS
nCount NUMBER;
BEGIN
SELECT COUNT(*)
INTO nCount
FROM CADEAUKAART
WHERE KAART_CODE = pinKAART_CODE ;
RETURN CASE
WHEN nCount > 0 THEN
TRUE
ELSE
FALSE
END;
END IS_KAART_CODE_VALID;

You may try a cursor, alternatively :
DECLARE
v_flag boolean := FALSE;
BEGIN
FOR c IN ( SELECT KAART_CODE FROM CADEAUKAART )
LOOP
IF :P10_KAART_CODE = c.KAART_CODE THEN
v_flag := TRUE;
EXIT;
END IF;
EXIT WHEN NO_DATA_FOUND;
END LOOP;
RETURN v_flag;
END;
By your way using a select statement is not allowed, you might list all the members for the returning values of KAART_CODE such as
IF :P10_KAART_CODE IN ('aAA','BBb','ccC'..) THEN
but which is not preferable and nice to list all matching values .

Related

Return value from case statement in plpgsql

How can I set a value (or just return a value directly) from a user defined function that uses a case statement?
create function is_bar(email varchar) returns boolean as
$$
declare returnValue boolean;
begin
select case when exists
(select * from (users join user_roles on users.userID = user_roles.userID)
where user_email=email and user_role='bar')
then (returnValue := TRUE);
else (returnValue := FALSE);
end;
return returnValue;
end;
$$ language plpgsql;
gives me:
ERROR: syntax error at or near ":="
LINE 8: then (returnValue := TRUE);
The reason of described issue is change of SQL (functional) CASE statement and PLpgSQL (procedural) CASE statement.
The SQL CASE (functional):
BEGIN
RETURN CASE WHEN EXISTS(..)
THEN true /* value */
ELSE false END; /* ended by END */
END;
The PLpgSQL (procedural) CASE:
BEGIN
CASE WHEN EXISTS(..)
THEN
RETURN true; /* statement */
ELSE
RETURN false;
END CASE; /* ended by END CASE */
END;
There are some other examples (same result):
a := CASE WHEN b < 10 THEN true ELSE false END;
a := b < 10;
CASE WHEN b < 10 THEN
a := true;
ELSE
a := false;
END CASE;
It would be much easier to return the result of the exists operator itself:
CREATE FUNCTION is_bar(email VARCHAR) RETURNS BOOLEAN AS
$$
BEGIN
RETURN EXISTS (SELECT *
FROM users
JOIN user_roles ON users.userID = user_roles.userID
WHERE user_email = email AND user_role='bar')
END;
$$ LANGUAGE plpgsql;

use a validation function for an element in apex 5.0

I am trying to implement a validation on an application item in APEX 5.0. I am using a PL/SQL function returning a boolean
begin
if exists (select id_referencia
from items
where id_referencia = :P2_REFERENCIA)
then
return true;
else
return false;
end if;
end;
When I submit my page, I get the following error
ORA-06550: line 2, column 4: PLS-00204: function or pseudo-column
'EXISTS' may be used inside a SQL statement only ORA-06550: line 2,
column 1: PL/SQL: Statement ignored
if exists isn't valid PL/SQL syntax.
You could do a count(*) instead
declare
l_cnt integer;
begin
select count(*)
into l_cnt
from items
where id_referencia = :P2_REFERENCIA;
if( l_cnt >= 1 )
then
return true;
else
return false;
end if;
end;
If you really wanted to do an exists, you'd do something like this. I don't know why you'd want to do this assuming that id_referencia is the primary key of the table. But you could
declare
l_exists integer;
begin
begin
select 1
into l_exists
from dual
where exists( select 1
from items
where id_referencia = :P2_REFERENCIA );
exception
when no_data_found
then
l_exists := 0;
end;
if( l_exists = 1 )
then
return true;
else
return false;
end if;
end;
If you want to use EXISTS in a PL/SQL function returning BOOLEAN,try this
DECLARE
v_exist NUMBER;
BEGIN
SELECT 1
INTO v_exist
FROM DUAL WHERE EXISTS (select id_referencia
from items
where id_referencia = :P2_REFERENCIA)
IF v_exist IS NOT NULL
THEN
RETURN true;
ELSE
RETURN false;
END IF;
END;

PL sql to find a string in a list

I have a problem because in this code I don't know how I can to test if a string is in a list which isn't initialized .
The code is this :
TYPE t1 IS TABLE OF VARCHAR2(32767) index by PLS_INTEGER;
v_t1 t1;
WOUT varchar2(80) :='bbbb';
v_t1(1):='bbbb';
if (WOUT member of v_t1 ) then
....
end if;
I don't know how i can write in plsql the condition that wout is inside v_t1 because member is accepted only with lists initialized.
Thanks for the help
With the collection declared as you have it there's no simple way to do what you're trying to do except to iterate through the collection to find the element you want:
declare
TYPE t1 IS TABLE OF VARCHAR2(32767)
index by PLS_INTEGER;
v_t1 t1;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN := FALSE;
BEGIN
v_t1(0) := 'aaaa';
v_t1(1) := 'bbbb';
v_t1(2) := 'cccc';
v_t1(26) := 'zzzz';
i := v_t1.FIRST;
LOOP
DBMS_OUTPUT.PUT_LINE('i=' || i);
IF v_t1(i) = WOUT THEN
bFound := TRUE;
EXIT;
END IF;
i := v_t1.NEXT(i);
IF i IS NULL THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
end;
HOWEVER - if you change the collection so that it's indexed by the string it contains you can accomplish this task a bit easier by using the EXISTS collection method:
declare
TYPE T2 IS TABLE OF VARCHAR2(32767)
INDEX BY VARCHAR2(32767);
v_t2 t2;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN;
BEGIN
v_t2('aaaa') := 'aaaa';
v_t2('bbbb') := 'bbbb';
v_t2('cccc') := 'cccc';
v_t2('zzzz') := 'zzzz';
bFound := v_t2.EXISTS(WOUT);
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
END;
The documentation on collection methods can be found here.
Share and enjoy.
You can create special function that checks if such value is inside collection. Something like that:
FUNCTION exist_in_collection( p_table t1, p_text VARCHAR2) RETURN boolean
IS
BEGIN
IF p_table.LAST IS NULL THEN
RETURN FALSE;
ELSE
FOR i IN p_table.FIRST.. p_table.LAST
LOOP
IF p_table(i) = p_text THEN
RETURN TRUE;
END IF;
END LOOP;
RETURN FALSE;
END IF;
END exist_in_collection;
if p_table is not initialized LAST is NULL you will get FALSE. And then you can use this function to check whether the string is inside collection:
IF exist_in_collection(p_table, WOUT) THEN ... END IF;

Display error message instead of 'no rows selected'

I want my function to display 'Empty Invoice Found!' Or 'Invoice Not Found' error messages.
But when I select the data with error(doesnt exist in the table), it return 'no rows selected'. Please guide me with the correct way to SELECT.
SET ECHO ON;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION totalInvoiceAmount(InvoiceNo IN INVOICE_ITEM.InvoiceNumber%TYPE)
RETURN NUMBER IS
TotalCost NUMBER(20);
CURSOR X_RECORD IS SELECT * FROM INVOICE_ITEM;
CURSOR Y_RECORD IS SELECT * FROM INVOICE_ITEM;
tempCost NUMBER:=0;
InvoiceCheck BOOLEAN:=FALSE;
InvoiceEmpty BOOLEAN:=TRUE;
BEGIN
FOR Record IN X_RECORD LOOP
IF Record.InvoiceNumber = InvoiceNo THEN
InvoiceCheck:=TRUE;
EXIT;
ELSE
InvoiceCheck:=FALSE;
END IF;
END LOOP;
FOR Record IN Y_RECORD LOOP
IF Record.InvoiceNumber = InvoiceNo AND InvoiceCheck=FALSE THEN
InvoiceEmpty:=TRUE;
EXIT;
ELSE
InvoiceEmpty:=FALSE;
END IF;
END LOOP;
IF InvoiceCheck=FALSE AND InvoiceEmpty=FALSE THEN
DBMS_OUTPUT.PUT_LINE('Invoice Not Found!');
RETURN -1;
ELSIF InvoiceCheck=FALSE AND InvoiceEmpty=TRUE THEN
DBMS_OUTPUT.PUT_LINE('Empty Invoice Found!');
RETURN 0;
ELSE
FOR Record IN X_RECORD LOOP
IF Record.InvoiceNumber = InvoiceNo THEN
tempCost:=tempCost+(Record.UnitPrice*Record.Quantity);
END IF;
END LOOP;
TotalCost:=tempCost;
RETURN TotalCost;
END IF;
END;
/
SELECT invoicenumber,totalInvoiceAmount(invoicenumber) AS "Total Cost" FROM INVOICE_ITEM Where invoicenumber=2011011;
SELECT invoicenumber,totalInvoiceAmount(invoicenumber) AS "Total Cost" FROM INVOICE_ITEM where invoicenumber=2011010;
The messages you want come from a function which you are calling in a SELECT statement. So if that SELECT returns no rows there are are no rows which can be used to call that function.
I'm afraid it's not clear what you're trying to achieve, but whatever it is you need a different approach. Having said which I suggest something like this might be give you what you want (in a more orthodox and more efficient fashion):
SELECT invoicenumber,
sum(UnitPrice*Quantity) AS "Total Cost"
FROM INVOICE_ITEM
where invoicenumber=2011010
group by InvoiceNumber;
SET ECHO ON;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION totalInvoiceAmount(InvoiceNo IN INVOICE_ITEM.InvoiceNumber%TYPE)
RETURN NUMBER IS
TotalCost NUMBER(20);
invoice_not_found EXCEPTION ;
empty_invoice_found EXCEPTION ;
CURSOR X_RECORD IS SELECT * FROM INVOICE_ITEM;
CURSOR Y_RECORD IS SELECT * FROM INVOICE_ITEM;
tempCost NUMBER:=0;
InvoiceCheck BOOLEAN:=FALSE;
InvoiceEmpty BOOLEAN:=TRUE;
BEGIN
FOR Record IN X_RECORD LOOP
IF Record.InvoiceNumber = InvoiceNo THEN
InvoiceCheck:=TRUE;
EXIT;
ELSE
InvoiceCheck:=FALSE;
END IF;
END LOOP;
FOR Record IN Y_RECORD LOOP
IF Record.InvoiceNumber = InvoiceNo AND InvoiceCheck=FALSE THEN
InvoiceEmpty:=TRUE;
EXIT;
ELSE
InvoiceEmpty:=FALSE;
END IF;
END LOOP;
IF InvoiceCheck=FALSE AND InvoiceEmpty=FALSE THEN
--DBMS_OUTPUT.PUT_LINE('Invoice Not Found!');
RAISE invoice_not_found;
--RETURN -1;
ELSIF InvoiceCheck=FALSE AND InvoiceEmpty=TRUE THEN
--DBMS_OUTPUT.PUT_LINE('Empty Invoice Found!');
RAISE empty_invoice_found
RETURN 0;
ELSE
FOR Record IN X_RECORD LOOP
IF Record.InvoiceNumber = InvoiceNo THEN
tempCost:=tempCost+(Record.UnitPrice*Record.Quantity);
END IF;
END LOOP;
TotalCost:=tempCost;
RETURN TotalCost;
END IF;
EXCEPTION
when invoice_not_found then
DBMS_OUTPUT.PUT_LINE('Invoice Not Found!');
raise -1;
when empty_invoice_found then
DBMS_OUTPUT.PUT_LINE('Empty Invoice Found!');
raise 0;
END;
You can catch it in an exception and handle it accordingly , let me know if it works

ORA-06550: line 1, column 7 (PL/SQL: Statement ignored) Error

I am getting following error for the stored procedure and not able to understand the issue (must be from db side) While googling, I found similar issues but couldn't get the solution. Can any one help me please find the error in PROCEDURE ??
Error :-
18:58:50,281 ERROR [STDERR] java.sql.SQLException: ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'SP_DIST_RETAILER_REMAP'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
Stored Prodedure(SP_DIST_RETAILER_REMAP) :-
CREATE OR REPLACE PROCEDURE SMAPRD02.SP_DIST_RETAILER_REMAP (
i_old_dist_code IN VARCHAR2,
i_new_dist_code IN VARCHAR2,
i_territory_remapping IN NUMBER,
i_remapping_reason IN VARCHAR2,
i_trans_doneby_rolename IN VARCHAR2,
i_trans_doneby_id IN NUMBER,
i_trans_dist_rolename IN VARCHAR2,
i_trans_ret_rolename IN VARCHAR2,
i_activity_type IN VARCHAR2,
i_ret_list IN V_ARRAY,
result OUT VARCHAR2,
i_o_query OUT VARCHAR2
)
AS
--i_ret_codes OUT VARCHAR2;
v_dist_count NUMBER;
v_ret_count NUMBER;
v_ret_codes VARCHAR2(10000) := '';
v_flag VARCHAR2(10) := 'true';
v_trans_id NUMBER;
v_query VARCHAR2(10000);
BEGIN
IF i_territory_remapping = 1 then
SELECT count(*) into v_dist_count FROM tblemployee where EMPCODE = i_new_dist_code and circle_code = (select emp.circle_code
from tblemployee emp where emp.empcode = i_old_dist_code) and upper(user_type) like upper('%dist%') and upper(ACCESS_TO) in ('SALES','BOTH') and upper(stage) not in (upper('InActive'));
ELSE
SELECT count(*) into v_dist_count FROM tblemployee where EMPCODE = i_new_dist_code and circle_code = (select emp.circle_code from tblemployee emp
where emp.empcode = i_old_dist_code) and cluster_code = (select emp.cluster_code from tblemployee emp where emp.empcode = i_old_dist_code)
and upper(user_type) like upper('%dist%') and upper(ACCESS_TO) in ('SALES','BOTH') and upper(stage) not in (upper('InActive'));
END IF;
IF v_dist_count =0 THEN
result := 'invalid_new_dist_code';
v_flag := 'false';
ELSIF v_dist_count = 1 THEN
SELECT count(*) into v_ret_count FROM tblretailer t where t.DIST_CODE = i_old_dist_code and (upper(t.ACCESS_TO) = 'SALES' or upper(t.ACCESS_TO) = 'BOTH');
--SELECT count(*) into v_ret_count FROM tblretailer t where t.DIST_CODE = i_old_dist_code and upper(t.ACCESS_TO) = 'SALES' and upper(t.stage) in ('APPROVED','INACTIVE');
IF v_ret_count=i_ret_list.count THEN
IF i_territory_remapping = 1 THEN
result := 'no_ret_left';
v_flag := 'false';
END IF;
ELSE
IF i_territory_remapping != 1 THEN
result := 'ret_left';
v_flag := 'false';
END IF;
END IF;
END IF;
IF i_ret_list is null or i_ret_list.count = 0 THEN
result := 'empty retailers list';
v_flag := 'false';
END IF;
/*FOR i IN i_ret_list.FIRST .. i_ret_list.LAST
LOOP
IF v_ret_codes is null
THEN
v_ret_codes := ''''||i_ret_list(i)||'''';
ELSE
v_ret_codes := v_ret_codes||','''||i_ret_list(i)||'''';
END IF;
IF v_ret_codes is null
THEN
v_ret_codes := i_ret_list(i);
ELSE
v_ret_codes := v_ret_codes||','||i_ret_list(i);
END IF;
END LOOP;
i_ret_codes := v_ret_codes;
v_flag := 'false';
result := 'success';*/
IF v_flag = 'true' THEN
FOR i IN i_ret_list.FIRST .. i_ret_list.LAST
LOOP
IF v_ret_codes is null
THEN
v_ret_codes := ''''||i_ret_list(i)||'''';
ELSE
v_ret_codes := v_ret_codes||','''||i_ret_list(i)||'''';
END IF;
END LOOP;
--i_ret_codes := v_ret_codes;
--update tblretailer set dist_code=i_new_dist_code,DIST_ID=to_number(i_new_dist_code),cluster_code=(select cluster_code from tblemployee where empcode = i_new_dist_code),FOSID='',FOS_CODE='',DSR_ID='',DSR_CODE='',LAST_UPDATED_DATE=sysdate where retcode in (v_ret_codes);
v_query := 'update tblretailer set dist_code='||i_new_dist_code||',DIST_ID=to_number('||i_new_dist_code||'),cluster_code=(select cluster_code from tblemployee where empcode = '||i_new_dist_code||'),FOSID='''',FOS_CODE='''',DSR_ID='''',DSR_CODE='''',LAST_UPDATED_DATE=sysdate where retcode in ('||v_ret_codes||')';
execute immediate (v_query);
--i_query :='update tblretailer set dist_code='||i_new_dist_code||',DIST_ID=to_number('||i_new_dist_code||'),cluster_code=(select cluster_code from tblemployee where empcode = '||i_new_dist_code||'),FOSID='',FOS_CODE='',DSR_ID='',DSR_CODE='',LAST_UPDATED_DATE=sysdate where retcode in ('||v_ret_codes||');';
insert into TBL_TRANSFER_SUP_MASTER(MASTER_ID,TRANS_ID,TRANS_DONEBY_ROLENAME,TRANS_DONEBY_ID,TRANS_FROM_ROLENAME,TRANS_FROM,TRANS_TO_ROLENAME,TRANS_TO,ACTIVITY_CODE,TRANS_DATE,TRANSFER_REASON,LAST_UPDATED_DATE)
values(SUP_MASTER_TRANS_ID_SEQ.nextval,SUP_MASTER_TRANS_ID_SEQ.nextval,i_trans_doneby_rolename,i_trans_doneby_id,i_trans_dist_rolename,i_old_dist_code,i_trans_dist_rolename,i_new_dist_code,'101',sysdate,i_remapping_reason,sysdate) return TRANS_ID into v_trans_id;
FOR i IN i_ret_list.FIRST .. i_ret_list.LAST
LOOP
insert into TBL_TRANSFER_SUP_DTLS(DTLS_ID,TRANS_ID,TRANS_ON_ROLENAME,TRANS_ON_ID,LAST_UPDATED_DATE)
values(SUP_DTLS_ID_SEQ.nextval,v_trans_id,i_trans_ret_rolename,i_ret_list(i),sysdate);
END LOOP;
IF SQL%ROWCOUNT>0 THEN
result := 'success';
ELSE
result := 'failure';
END IF;
--update tblstock set NEW_DIST_CODE_REMAP=i_new_dist_code,REMAP_DATE=sysdate,LAST_UPDATED_DATE=sysdate where (DIST_CODE=i_old_dist_code or NEW_DIST_CODE_REMAP=i_old_dist_code) and RET_CODE in (v_ret_codes);
v_query := 'update tblstock set NEW_DIST_CODE_REMAP='||i_new_dist_code||',REMAP_DATE=sysdate,LAST_UPDATED_DATE=sysdate where (DIST_CODE='||i_old_dist_code||' or NEW_DIST_CODE_REMAP='||i_old_dist_code||') and RET_CODE in ('||v_ret_codes||')';
execute immediate (v_query);
i_o_query := v_query;
insert all into TBL_ACTIVITY_LOG (LOG_ID,TRANS_ID,ACTIVITY_DONEBY_ROLENAME,ACTIVITY_DONEBY_ID,ACTIVITY_REFERENCE_ID,ACTIVITY_CODE,ACTIVITY_DATE)
values(ACTIVITY_LOG_TRANS_ID_SEQ.NEXTVAL,ACTIVITY_LOG_TRANS_ID_SEQ.NEXTVAL,i_trans_doneby_rolename,i_trans_doneby_id,v_trans_id,
act_code,sysdate) select log_config.ACTIVITY_CODE act_code from TBL_ACTIVITY_LOG_CONFIG log_config
where upper(log_config.ACTIVITY_TYPE)= upper(i_activity_type);
END IF;
END;
/
Java Code :-
try{
if(ret_list.size()>0)
ret_code = ret_list.toArray();
con = ConnectionManager.getDirectConnection();
ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor(PropertyLoader.RET_SECONDARY_V_ARRAY,con);
ARRAY array_to_pass = new ARRAY( descriptor,con, ret_code );
cstmt = con.prepareCall("{ call SP_DIST_RETAILER_REMAP(?,?,?,?,?,?,?,?,?,?,?,?)}");
cstmt.setString(1,old_dist_code.trim());
cstmt.setString(2,new_dist_code.trim());
if(territory_remapping)
cstmt.setInt(3,1);
else
cstmt.setInt(3,2);
cstmt.setString(4,remapping_reason);
cstmt.setString(5,userVO.getRolename().trim());
cstmt.setInt(6,userVO.getEmpid());
cstmt.setString(7,PropertyLoader.DISTRIBUOTR_ROLENAME);
cstmt.setString(8,PropertyLoader.RETAILER_ROLENAME);
cstmt.setString(9,PropertyLoader.ACTIVITY_TYPES_RETAILER_REMAPPING);
cstmt.setArray(10,array_to_pass);
cstmt.registerOutParameter(11,Types.VARCHAR);
cstmt.registerOutParameter(12,Types.VARCHAR);
/*cstmt.registerOutParameter(13,Types.VARCHAR);*/
cstmt.execute();
status = cstmt.getString(11);
System.out.println("Remap Update Query "+cstmt.getString(12));
//System.out.println(cstmt.getString(13));
}
If the value stored in PropertyLoader.RET_SECONDARY_V_ARRAY is not "V_ARRAY", then you are using different types; even if they are declared identically (e.g. both are table of number) this will not work.
You're hitting this data type compatibility restriction:
You can assign a collection to a collection variable only if they have
the same data type. Having the same element type is not enough.
You're trying to call the procedure with a parameter that is a different type to the one it's expecting, which is what the error message is telling you.
The procedure which you are using is should be properly declared in the place which you are using it. For an example if it is under a user and in a package then it should be in that order. The username, package and procedurename.