Getting Unknown Command error on IF-THEN-ELSE - oracle

I have the following query that I am using in Oracle 11g
IF EXISTS (SELECT * FROM EMPLOYEE_MASTER WHERE EMPID='ABCD32643')
THEN
update EMPLOYEE_MASTER set EMPID='A62352',EMPNAME='JOHN DOE',EMPTYPE='1' where EMPID='ABCD32643' ;
ELSE
insert into EMPLOYEE_MASTER(EMPID,EMPNAME,EMPTYPE) values('A62352','JOHN DOE','1') ;
END IF;
On running the statement I get the following output:
Error starting at line : 4 in command -
ELSE
Error report -
Unknown Command
1 row inserted.
Error starting at line : 6 in command -
END IF
Error report -
Unknown Command
The values get inserted with error when I run it directly. But when I try to execute this query through my application I get an oracle exception because of the error generated :
ORA-00900: invalid SQL statement
And hence the values are not inserted.
I am relatively new to Oracle. Please advise on what's wrong with the above query so that I could run this query error free.

If MERGE doesn't work for you, try the following:
begin
update EMPLOYEE_MASTER set EMPID='A62352',EMPNAME='JOHN DOE',EMPTYPE='1'
where EMPID='ABCD32643' ;
if SQL%ROWCOUNT=0 then
insert into EMPLOYEE_MASTER(EMPID,EMPNAME,EMPTYPE)
values('A62352','JOHN DOE','1') ;
end if;
end;
Here you you the update on spec, then check whether or not you found a matching row, and insert in case you didn't.

"what's wrong with the above query "
What's wrong with the query is that it is not a query (SQL). It should be a program snippet (PL/SQL) but it isn't written as PL/SQL block, framed by BEGIN and END; keywords.
But turning it into an anonymous PL/SQL block won't help. Oracle PL/SQL does not support IF EXISTS (select ... syntax.
Fortunately Oracle SQL does support MERGE statement which does the same thing as your code, with less typing.
merge into EMPLOYEE_MASTER em
using ( select 'A62352' as empid,
'JOHN DOE' as empname,
'1' as emptype
from dual ) q
on (q.empid = em.empid)
when not matched then
insert (EMPID,EMPNAME,EMPTYPE)
values (q.empid, q.empname, q.emptype)
when matched then
update
set em.empname = q.empname, em.emptype = q.emptype
/
Except that you're trying to update empid as well. That's not supported in MERGE. Why would you want to change the primary key?
"Does this query need me to add values to all columns in the table? "
The INSERT can have all the columns in the table. The UPDATE cannot change the columns used in the ON clause (usually the primary key) because that's a limitation of the way MERGE works. I think it's the same key preservation mechanism we see when updating views. Find out more.

Related

How to validate a query without executing in PowerBuilder

wondering if there is way to validate a query before executing
Is there way to check/validate Query without executing it?
One way that we validate SQL is to add a condition to the SQL that could never be true.
Example:
long ll_rc
long ll_result
string ls_sql, ls_test
string ls_message
//Arbitrary SQL
ls_sql = "SELECT * FROM DUAL"
//This SQL when executed will always return 0 if successful.
ls_test = "select count(*) from ( " + ls_sql + " WHERE 1 = 2 )"
DECLARE l_cursor DYNAMIC CURSOR FOR SQLSA ;
PREPARE SQLSA FROM :ls_test;
OPEN DYNAMIC l_cursor;
ll_rc = SQLCA.SQLCODE
choose case ll_rc
case 0
//Success
ls_message = "SQL is properly formed"
case 100
//Fetched row not found. This should not be the case since we only opened the cursor
ls_message = SQLCA.SQLERRTEXT
case -1
//Error; the statement failed. Use SQLErrText or SQLDBCode to obtain the detail.
ls_message = SQLCA.SQLERRTEXT
end choose
CLOSE l_cursor ; //This will fail if open cursor failed.
messagebox( "Result", ls_message )
Note: If your SQL is VERY complicated, which I suspect it isn't, the database optimizer may take several seconds to prepare your SQL. It will be significantly less time than if you run the entire query.
Since the database is the final arbitrator for what is "valid" (table and column names and such) the general answer is no. Now you could come up with a class in PB which checks statement syntax, object names, etc. so you wouldn't have to touch the db but it would be obsolete as soon as any changes were made to the db.
Put the select statement in any script and compile it. Part of the work will be to check the SQL syntax against the database you are connected to.
Watch out: you need at least one bound variable in the column list of your SQL statement. This is not the case for other DML statements.
Example:
in my case:
select noms into :ls_ttt from contacts;
results in a message Unknown columns 'noms' in 'field list'.
However,
select nom into :ls_ttt from contacts;
does not show any error.
Hope this helps.

Issue while executing stored procedure which consists both update and insert statements

I am new to PLSQL and I am trying to execute this stored procedure shown here.
This stored procedure will check for a particular row and based on the count update the table or insert. But I am getting below errors as a whole.
31/18 PL/SQL: ORA-00928: missing SELECT keyword
31/1 PL/SQL: SQL Statement ignored
37/26 PL/SQL: ORA-00933: SQL command not properly ended
36/1 PL/SQL: SQL Statement ignored
I tried my best to solve them. Could you please help in solving the issue?
This is the procedure I have written for this task:
CREATE OR REPLACE PROCEDURE LPR_LP_TEST.SP_PTMS_NOTES
(
p_app_lse_s IN mjl.app_lse_s%TYPE,
p_dt_ent_s IN mjl.dt_ent_s%TYPE,
p_note_type_s IN mjl.note_type_s%TYPE,
p_prcs_c IN mjl.prcs_c%TYPE,
p_prio_c IN mjl.prio_c%TYPE,
p_note_title_s IN mjl.note_title_s%TYPE,
p_info1_s IN mjl.info1_s%TYPE,
p_info2_s IN mjl.info2_s%TYPE
)
AS
v_rowcount_i number;
v_lien_date mjl.info1_s%TYPE;
--v_lien_date NMAC_PTMS_NOTEBK_SG.LIEN_DT%TYPE;
v_asst_amount mjl.info2_s%TYPE;
BEGIN
app_lse_s:=trim(app_lse_s);
dbms_output.put_line(app_lse_s);
select LIEN_DT,ASES_PRT_1_AM
INTO v_lien_date,v_asst_amount
from NMAC_PTMS_NOTEBK_SG
where LSE_ID ='&2';
select count(*) into v_rowcount_i from MJL where trim(app_lse_s) ='&2';
if v_rowcount_i = 0 then
begin
Insert into MJL
('app_lse_s','dt_ent_s','note_type_s','prcs_c','prio_c','note_title_s','info
1_s','Info2_s')
values ('&2','sysdate','SPPT','Y','1','Property Tax
Assessment','v_lien_date','v_asst_amount');
end;
else
begin
update mjl
set dt_ent_s = 'sysdate' and note_type_s = 'SPPT' and prcs_c = 'Y' and
prio_c = '1' and note_title_s = 'Property Tax Assessment' and info1_s =
'v_lien_date' and Info2_s = 'v_asst_amount'
where trim(app_lse_s) = '&2';
end;
end if;
commit;
end;
/
I believe your procedure should look something like:
CREATE OR REPLACE PROCEDURE LPR_LP_TEST.SP_PTMS_NOTES
(
p_app_lse_s IN mjl.app_lse_s%TYPE,
p_dt_ent_s IN mjl.dt_ent_s%TYPE,
p_note_type_s IN mjl.note_type_s%TYPE,
p_prcs_c IN mjl.prcs_c%TYPE,
p_prio_c IN mjl.prio_c%TYPE,
p_note_title_s IN mjl.note_title_s%TYPE,
p_info1_s IN mjl.info1_s%TYPE,
p_info2_s IN mjl.info2_s%TYPE
)
AS
v_rowcount_i number;
v_lien_date mjl.info1_s%TYPE;
--v_lien_date NMAC_PTMS_NOTEBK_SG.LIEN_DT%TYPE;
v_asst_amount mjl.info2_s%TYPE;
v_app_lse_s mjl.app_lse_s%TYPE;
BEGIN
v_app_lse_s := trim(p_app_lse_s);
-- I hope this dbms_output line is for temporary debug purposes only
-- and will be removed in the production version!
dbms_output.put_line(app_lse_s);
merge into mjl tgt
using (select lse_s app_lse_s,
sysdate dt_ent_s,
'SPPT' note_type_s,
'Y' prcs_c,
'1' prio_c,
'Property Tax Assessment' note_title_s,
lien_dt info1_s,
ases_prt_1_am info2_s
from nmac_ptms_notebk_sg
where lse_id = v_app_lse_s) src
on (tgt.app_lse_s = src.app_lse_s)
when matched then
update set tgt.dt_ent_s = src.dt_ent_s,
tgt.note_title_s = src.note_title_s,
tgt.info1_s = src.info1_s,
tgt.info2_s = src.info2_s
where tgt.dt_end_s != src.dt_ent_s
or tgt.note_title_s != src.note_title_s
or tgt.info1_s != src.info1_s
or tgt.info2_s != src.info2_s
when not matched then
insert (tgt.app_lse_s,
tgt.dt_ent_s,
tgt.note_type_s,
tgt.prcs_c,
tgt.prio_c,
tgt.note_title_s,
tgt.info1_s,
tgt.info2_s)
values (src.app_lse_s,
src.dt_ent_s,
src.note_type_s,
src.prcs_c,
src.prio_c,
src.note_title_s,
src.info1_s,
src.info2_s);
commit;
end;
/
Things for you to note about your procedure and what I did to come up with the above procedure:
You have a tendency to enclose everything in single quotes. Single quotes are used to declare something as a string, i.e. some_variable := 'string value'. If you put single quotes around something that is actually an identifier, you are really telling Oracle to treat it as a string - which will result in all sorts of errors! The only time you should use quotes around an identifier is when the identifier's name is case sensitive, and you should use double-quotes. E.g. select * from "lower_case_tablename". (N.B. I say "should" here, but that's a guideline; you can use double-quotes around non-case-sensitive identifier names, but if you do so, the name should be in uppercase - i.e. select * from "DUAL";).
Your update statement syntax was incorrect - updates to several columns in a single statement are separated by commas, not ands.
The begins and ends around your insert and update statements are unnecessary.
If you're going to have an implicit cursor (i.e. the select ... into <variable list> from ... in the procedure body), you need to make sure you handle the NO_DATA_FOUND and TOO_MANY_ROWS exceptions that might be thrown up.
I set up a variable to store the trimmed value passed in by the parameter p_app_lse_s - I assume that this is what you meant to do? I also replaced all the calls to '&2' with the variable.
If you need to do an upsert (i.e. insert if the row doesn't already exist, otherwise update) then consider a MERGE statement. If you absolutely must keep them separate, then don't check for the existence of the row first; do the insert first and check for a DUP_VAL_ON_INDEX error - then do the update in the exception handler. Alternatively, do the update first and check SQL%ROWCOUNT to see if rows were amended and if not, then do the insert. A MERGE is preferable, though, since it means there's no opportunity for someone to insert a row in a different session in the split second it takes the database to go between the two statements.
By using a MERGE statement, I was able to incorporate all your logic into a single SQL statement, which makes your procedure simpler and easier to debug. For a start, I'm betting the other parameters in your procedure need to be used inside the procedure; it's easy to update the source query in the merge statement to replace the hardcoded values with the parameter names! I'll leave that as an exercise for you to do.
If you're getting the info1_s and info2_s values from the nmac_ptms_notebk_sg, do you really need the p_info1_s and p_info2_s parameters? They wouldn't seem to be needed, IMHO.
Finally, this procedure is doing the work a single app_lse_s at a time. If your database processing is OLTP, that's fine. If it's doing batch processing, and your code looks something like the following pseudo-code:
for each row in <this cursor>
loop
execute the lpr_lp_test.sp_ptms_notes procedure
end loop
then you'd be better off merging the sp_ptms_notes procedure into the calling procedure and doing the work in a single MERGE statement.
ETA: If you have a staging table (which could be an external table or a Global Temporary Table (GTT) or even a normal heap table) that contains the data you want to load into your database, then your merge statement would become something like:
merge into mjl tgt
using (select trim(st.app_lse_s) app_lse_s,
sysdate dt_ent_s,
'SPPT' note_type_s,
'Y' prcs_c,
'1' prio_c,
'Property Tax Assessment' note_title_s,
npns.lien_dt info1_s,
npns.ases_prt_1_am info2_s
from staging_table st
inner join nmac_ptms_notebk_sg npns-- maybe left outer join?
on trim(st.app_lse_s) = npns.lse_s) src
on (tgt.app_lse_s = src.app_lse_s)
when matched then
update set tgt.dt_ent_s = src.dt_ent_s,
tgt.note_title_s = src.note_title_s,
tgt.info1_s = src.info1_s,
tgt.info2_s = src.info2_s
where tgt.dt_end_s != src.dt_ent_s
or tgt.note_title_s != src.note_title_s
or tgt.info1_s != src.info1_s
or tgt.info2_s != src.info2_s
when not matched then
insert (tgt.app_lse_s,
tgt.dt_ent_s,
tgt.note_type_s,
tgt.prcs_c,
tgt.prio_c,
tgt.note_title_s,
tgt.info1_s,
tgt.info2_s)
values (src.app_lse_s,
src.dt_ent_s,
src.note_type_s,
src.prcs_c,
src.prio_c,
src.note_title_s,
src.info1_s,
src.info2_s);
You can see that I've joined the nmac_ptms_notebk_sg table to the staging table, and used that to generate the set of data that needs to be merged into your mjl table. If your file/staging table also contains information for the other columns (dt_ent_s, note_type_s, etc) then you can replace the hardcoded values with the columns from the staging table.

ORA-01403: no data found IN ORACLE PL/SQL

I have an oracle error executing this PL/SQL in the second line: SELECT ....
But for God's sake ! I've already check if there is some null values
IF zocRole IS NOT NULL and devices.unit_id IS NOT NULL THEN
SELECT unit_role_id INTO unitRoleId FROM T_UNIT_ROLE WHERE role_id = zocRole AND unit_id = devices.unit_id;
END IF;
As mentioned above, this exception is thrown because your implicit cursor returns no rows. You would also get an exception if more than one row is returned.
You could instead use an Explicit Cursor Oracle Documents This is really just a named SQL statement (into which you can pass parameters if you like).
You then open the cursor, fetch (each fetch will attempt to retrieve one row) and close. You can then check whether the fetch returned any data. It takes slightly longer to code but can look cleaner.
I remember that years ago there was some debate about the relative speed of implicit vs explicit cursors but I've not heard anyone talk about this for a long time so I assume they perform the same
The best way to control the execution in a procedure/function plsql is adding blocks: BEGIN/EXCEPTION.
IF zocRole IS NOT NULL AND devices.unit_id IS NOT NULL
THEN
BEGIN
SELECT unit_role_id
INTO unitRoleId
FROM T_UNIT_ROLE
WHERE role_id = zocRole
AND unit_id = devices.unit_id
;
EXCEPTION
WHEN OTHERS
THEN dbms_output.put_line(SQLCODE||'-'||SUBSTR(SQLERRM, 1, 200));
END
;
END IF
;

How to create an Oracle stored procedure with a parameter?

I'm a novice at SQL and am trying to create a Stored Procedure in Oracle database. The SPROC needs two date parameters (from_date and to_date) for my report to run. Maybe I'm confusing this with SQL Server code.
My code looks like this:
CREATE PROCEDURE uSP_RevPerSalesman
#from_date DATE
#to_date DATE
AS
BEGIN
SELECT DISTINCT
C.CUSTOMER_CODE
, MS.SALESMAN_NAME
, SUM(C.REVENUE_AMT)
FROM
C_REVENUE_ANALYSIS C
, M_CUSTOMER MC
, M_SALESMAN MS
WHERE
C.CUSTOMER_CODE = MC.CUSTOMER_CODE AND
MC.SALESMAN_CODE = MS.SALESMAN_CODE AND
MC.COMP_CODE = 'W1' AND
MS.COMP_CODE = '00' AND
C.REVENUE_DATE >= :from_date AND
C.REVENUE_DATE <= :to_date
GROUP BY
C.CUSTOMER_CODE, MS.SALESMAN_NAME
ORDER BY
C.CUSTOMER_CODE
END
GO
I get an error message when I run this code. The error message I get is:
ERROR ORA-00900: invalid SQL statement
When I run only the SELECT code, it works and gives me the right results. I just can't seem to make this into a SPROC.
Remove the GO, that is not valid in Oracle. Try a semicolon at the end instead, or a /, depending on where you're running this.

Syntax of if exists in IBM Db2

The follow query drops a table if the table exists but it doesnt seem to work for IBM Db2.
Begin atomic
if( exists(
SELECT 1 FROM SYSIBM.SYSTABLES
WHERE NAME='EMAIL' AND TYPE='T' AND creator = 'schema1'
)) then
drop table EMAIL;
end if;
End
Whereas the same if exists syntax works if i have a DML statement instead of table drop statement. Any help on this is appreciated
Update 1: I read that you cannot run DDL statement within begin atomic block hence my first statement fails but the second goes fine.
The way i did it is as follows
Begin atomic
if( exists( SELECT 1
FROM SYSIBM.SYSTABLES
WHERE NAME='EMAIL' AND TYPE='T' AND creator = 'schema1'
)
)
then customStoredproc('drop table EMAIL');
end if;
End
My customStoredProc just has one stmt execute immediate #dynsql;
You are correct that DB2 prohibits DDL within an atomic SQL block. IBM has released a free add-on procedure called db2perf_quiet_drop that works the way you want.
In case if you looking for embedded SQL:
Exec SQL
update Table1 set TabCol1 ='New Value'
where Table1KeyField1 =:Table1KeyValue1
and Table1KeyField2 =:Table1KeyValue2
and Exists (
select '1' from Table2
where Table2KeyField1 =:Table2KeyValue1
and Table2KeyField2 =:Table2KeyValue2
) ;

Resources