INSERT OUTPUT Oracle - oracle

In a SSIS package I build, I need to capture the output of a update clause in Oracle, in order to send a warning email
I have read the related question Is there an Oracle equivalent to SQL Server's OUTPUT INSERTED.*? But it doesn't give me a proper result set, that I can capture through a Execute SQL task
DECLARE
TYPE ra_InfoErrorMail is RECORD
(LFUKID Crpdta.F580002.LFUKID%TYPE
,LFAA10 Crpdta.F580002.LFAA10%TYPE
,LFJOBDETLS Crpdta.F580002.LFJOBDETLS%TYPE);
TYPE ta_InfoErrorMail is TABLE OF ra_InfoErrorMail;
t_InfoErrorMail ta_InfoErrorMail;
BEGIN
UPDATE CRPDTA.F580002 SET LFKY = 'ERROR' WHERE LFAA10 = 'MYPROJECT' AND LFUSER ='MYUSER'
RETURNING LFUKID,LFAA10,LFJOBDETLS BULK COLLECT INTO t_InfoErrorMail;
--SELECT LFUKID,LFAA10,LFJOBDETLS FROM t_InfoErrorMail t; -- this doesn't work
END;
How do I get the whole t_InfoErrorMail in a neat SSIS-capturable ResultSet ?

Related

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.

Getting Unknown Command error on IF-THEN-ELSE

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.

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.

Parameter for IN query oracle [duplicate]

This question already has an answer here:
Oracle: Dynamic query with IN clause using cursor
(1 answer)
Closed 8 years ago.
SELECT * FROM EMPLOYEE
WHERE EMP_NAME IN (:EMP_NAME);
This is my query and now the EMP_NAME parameter I would like to send it as a list of strings.
When I run this query in SQL developer it is asked to send the EMP_NAME as a parameter, Now I want to send 'Kiran','Joshi' (Basically, I want to fetch the details of the employee with employee name either Kiran or Joshi. How should I pass the value during the execution of the query?
It works when I use the value Kiran alone, but when I concatenate with any other string it won't work. Any pointers in this?
I tried the one below
'Kiran','Joshi'
The above way doesn't work as understood this is a single parameter it tries the employee with the name as 'Kiran',Joshi' which won't come. Understandable, but in order to achieve this thing, how can I go ahead?
Any help would be really appreciated.
Thanks to the people who helped me in solving this problem.
I could get the solution using the way proposed, below is the approach
SELECT * FROM EMPLOYEE WHERE EMP_NAME IN (&EMP_NAME)
I have tried in this way and following are the scenarios which I have tested and they are working fine.
Scenario 1:
To fetch details of only "Kiran", then in this case the value of EMP_NAME when sql developer prompts is given as Kiran. It worked.
Scenario 2:
To fetch details of either "Kiran" or "Joshi", then the value of EMP_NAME is sent as
Kiran','Joshi
It worked in this case also.
Thanks Kedarnath for helping me in achieving the solution :)
IN clause would be implicitly converted into multiple OR conditions.. and the limit is 1000.. Also query with bind variable means, the execution plan will be reused.. Supporting bind variables for IN clause will hence affect the bind variable's basic usage, and hence oracle limits it at syntax level itself.
Only way is like name in (:1,:2) and bind the other values..
for this, you might dynamic SQL constructing the in clause bind variables in a loop.
Other way is, calling a procedure or function(pl/sql)
DECLARE
v_mystring VARCHAR(50);
v_my_ref_cursor sys_refcursor;
in_string varchar2='''Kiran'',''Joshi''';
id2 varchar2(10):='123'; --- if some other value you have to compare
myrecord tablename%rowtype;
BEGIN
v_mystring := 'SELECT a.*... from tablename a where name= :id2 and
id in('||in_string||')';
OPEN v_my_ref_cursor FOR v_mystring USING id2;
LOOP
FETCH v_my_ref_cursor INTO myrecord;
EXIT WHEN v_my_ref_cursor%NOTFOUND;
..
-- your processing
END LOOP;
CLOSE v_my_ref_cursor;
END;
IN clause supports maximum of 1000 items. You can always use a table to join instead. That table might be a Global Temporary Table(GTT) whose data is visible to thats particular session.
Still you can use a nested table also for it(like PL/SQL table)
TABLE() will convert a PL/Sql table as a SQL understandable table object(an object actually)
A simple example of it below.
CREATE TYPE pr AS OBJECT
(pr NUMBER);
/
CREATE TYPE prList AS TABLE OF pr;
/
declare
myPrList prList := prList ();
cursor lc is
select *
from (select a.*
from yourtable a
TABLE(CAST(myPrList as prList)) my_list
where
a.pr = my_list.pr
order by a.pr desc) ;
rec lc%ROWTYPE;
BEGIN
/*Populate the Nested Table, with whatever collection you have */
myPrList := prList ( pr(91),
pr(80));
/*
Sample code: for populating from your TABLE OF NUMBER type
FOR I IN 1..your_input_array.COUNT
LOOP
myPrList.EXTEND;
myPrList(I) := pr(your_input_array(I));
END LOOP;
*/
open lc;
loop
FETCH lc into rec;
exit when lc%NOTFOUND; -- Your Exit WHEN condition should be checked afte FETCH iyself!
dbms_output.put_line(rec.pr);
end loop;
close lc;
END;
/

Oracle Get two variables returned from select query inside of a package

I modified the procedure to make it smaller but I really only want to run the select query once. This will reduce the cost of running the procedure. How can I get the prevContectID and nextContentID without running the query twice. This is replacing a previous procedure so I do not want to change the IN and OUT so I do not have to find every where it is being called.
procedure getSeq(theContentID IN table.contentID%type,
prevContentID OUT table.contentID%type,
nextContentID OUT table.contentID%type)
BEGIN
SELECT myPrev into prevContentID, myNext into nextContentID
from myTable
where contentID=theContentID;
RETURN;
END getSeq;
The shown procedure most likely doesn't compile. The correct syntax for SELECT ... INTO using several variables is:
SELECT myPrev, myNext INTO prevContentID, nextContentID
from myTable
where contentID = theContentID;
You can also use a cursor to fetch the values from myTable.For your approach you need to do proper exception handling ,when theContentID does not exists in myTable,because that will give you NO_DATA_FOUND exception.
PROCEDURE getSeq (theContentID IN table.contentID%TYPE,
prevContentID OUT table.contentID%TYPE,
nextContentID OUT table.contentID%TYPE)
IS
CURSOR getcontentID_cur
IS
SELECT myPrev, myNext
FROM myTable
WHERE contentID = theContentID;
BEGIN
OPEN getcontentID_cur;
FETCH getcontentID_cur
INTO prevContentID, nextContentID;
CLOSE getcontentID_cur;
END getSeq;

Resources