Oracle - Selecting from remote database taken from PL/SQL parameter - oracle

I am creating a procedure to merge two tables across different database instances. The DB_FROM and the DB_TO are given as parameters to the procedure. Everything else is hardcoded.
PROCEDURE MERGE_TABLE_1(DB_FROM, DB_TO) AS
BEGIN
MERGE INTO TABLE_1#DB_TO DSTN
USING (SELECT * FROM TABLE_1#DB_FROM) SRC
ON (DSTN.ID = SRC.ID)
WHEN MATCHED THEN
WHEN NOT MATCHED THEN
END MERGE_TABLE_1
I get the below error when I attempt to compile
Error(1): ORA-04052: error occurred when looking up remote object
TABLE_1#DB_TO ORA-00604: error occurred at recursive SQL level 1
ORA-02019: connection description for remote database not found

Nope, it will not work that way. You cannot use variables as table names, column names or db link names in static SQL. You can achieve what you want using dynamic sql:
EXECUTE IMMEDIATE
'MERGE INTO TABLE_1#' || DB_TO || ' DSTN
USING (SELECT * FROM TABLE_1#' || DB_FROM || ') SRC
ON (DSTN.ID = SRC.ID)
WHEN MATCHED THEN
WHEN NOT MATCHED THEN ...';

Related

Is there a hint to generate execution plan ignoring the existing one from shared pool?

Is there a hint to generate execution plan ignoring the existing one from the shared pool?
There is not a hint to create an execution plan that ignores plans in the shared pool. A more common way of phrasing this question is: how do I get Oracle to always perform a hard parse?
There are a few weird situations where this behavior is required. It would be helpful to fully explain your reason for needing this, as the solution varies depending why you need it.
Strange performance problem. Oracle performs some dynamic re-optimization of SQL statements after the first run, like adaptive cursor sharing and cardinality feedback. In the rare case when those features backfire you might want to disable them.
Dynamic query. You have a dynamic query that used Oracle data cartridge to fetch data in the parse step, but Oracle won't execute the parse step because the query looks static to Oracle.
Misunderstanding. Something has gone wrong and this is an XY problem.
Solutions
The simplest way to solve this problem are by using Thorsten Kettner's solution of changing the query each time.
If that's not an option, the second simplest solution is to flush the query from the shared pool, like this:
--This only works one node at a time.
begin
for statements in
(
select distinct address, hash_value
from gv$sql
where sql_id = '33t9pk44udr4x'
order by 1,2
) loop
sys.dbms_shared_pool.purge(statements.address||','||statements.hash_value, 'C');
end loop;
end;
/
If you have no control over the SQL, and need to fix the problem using a side-effect style solution, Jonathan Lewis and Randolf Geist have a solution using Virtual Private Database, that adds a unique predicate to each SQL statement on a specific table. You asked for something weird, here's a weird solution. Buckle up.
-- Create a random predicate for each query on a specific table.
create table hard_parse_test_rand as
select * from all_objects
where rownum <= 1000;
begin
dbms_stats.gather_table_stats(null, 'hard_parse_test_rand');
end;
/
create or replace package pkg_rls_force_hard_parse_rand is
function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2;
end pkg_rls_force_hard_parse_rand;
/
create or replace package body pkg_rls_force_hard_parse_rand is
function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2
is
s_predicate varchar2(100);
n_random pls_integer;
begin
n_random := round(dbms_random.value(1, 1000000));
-- s_predicate := '1 = 1';
s_predicate := to_char(n_random, 'TM') || ' = ' || to_char(n_random, 'TM');
-- s_predicate := 'object_type = ''TABLE''';
return s_predicate;
end force_hard_parse;
end pkg_rls_force_hard_parse_rand;
/
begin
DBMS_RLS.ADD_POLICY (USER, 'hard_parse_test_rand', 'hard_parse_policy', USER, 'pkg_rls_force_hard_parse_rand.force_hard_parse', 'select');
end;
/
alter system flush shared_pool;
You can see the hard-parsing in action by running the same query multiple times:
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
Now there are three entries in GV$SQL for each execution. There's some odd behavior in Virtual Private Database that parses the query multiple times, even though the final text looks the same.
select *
from gv$sql
where sql_text like '%hard_parse_test_rand%'
and sql_text not like '%quine%'
order by 1;
I think there is no hint indicating that Oracle shall find a new execution plan everytime it runs the query.
This is something we'd want for select * from mytable where is_active = :active, with is_active being 1 for very few rows and 0 for maybe billions of other rows. We'd want an index access for :active = 1 and a full table scan for :active = 0 then. Two different plans.
As far as I know, Oracle uses bind variable peeking in later versions, so with a look at the statistics it really comes up with different execution plans for different bind varibale content. But in older versions it did not, and thus we'd want some hint saying "make a new plan" there.
Oracle only re-used an execution plan for exactly the same query. It sufficed to add a mere blank to get a new plan. Hence a solution might be to generate the query everytime you want to run it with a random number included in a comment:
select /* 1234567 */ * from mytable where is_active = :active;
Or just don't use bind variables, if this is the problem you want to address:
select * from mytable where is_active = 0;
select * from mytable where is_active = 1;

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.

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.

Merge error in Oracle 11g

I'm new at Oracle 11g and this forum. I just need to run a simple update statement using Oracle Merge statement because my SQL developer won't take it any other way.
The strange thing is, no matter how I try correcting the statement, even after reading many entries in this forum I still got the exact same error messages.
There are 2 different error messages, depend on how I run my statements in SQL Developer. If I run my statement as a store procedure (the update statement is a small part within a store procedure. I commented out other SQL statements and just left this update statement within the stored procedure) I got this error:
I ran this:
create or replace
PROCEDURE ADDR_UPDATE_2
AS
/* other sql statements are commented out */
MERGE INTO (SELECT id,xseq,line_1,line_2,zip,tax FROM ADDR ) b
USING (SELECT id,xseq,line_1, line_2,NVL(zip, '') AS pos_10 , NVL(tax, '') AS pos_11
FROM TEMP_ADDR ) v
ON(b.id = v.id
AND b.xseq = v.xseq)
WHEN MATCHED THEN UPDATE SET b.line_1 = v.line_1,
b.line_2 = v.line_2,
b.zip = v.pos_10,
b.tax = v.pos_11
WHERE b.line_1 <> v.line_1
OR b.line_2 <> v.line_2
OR b.zip <> v.pos_10;
/* other sql statements are commented out */
END;
and I got this error:
Error(23,9): PLS-00103: Encountered the symbol "INTO" when expecting one of the following: constant exception table long double ref char time timestamp interval date binary national character nchar
(I goggled everywhere and did not find any answer)
line 23 refers to the word "INTO"
But if I ran the above Merge statement by itself, NOT as part of a str. proc.,
I don't get any error. I got a result: 0 rows merged.
Now I modified the Merge statement into:
create or replace
PROCEDURE ADDR_UPDATE_2
AS
/* other sql statements are commented out */
MERGE INTO ADDR b
USING (SELECT id,xseq, line_1, line_2, NVL(zip, '') AS pos_10,
NVL(tax, '') AS pos_11
FROM TEMP_ADDR) v
ON(b.id = v.id
AND b.xseq = v.xseq
AND b.line_1 <> v.line_1
OR b.line_2 <> v.line_2
OR b.zip <> v.pos_10)
WHEN MATCHED THEN UPDATE SET b.line_1 = v.line_1,
b.line_2 = v.line_2,
b.zip = v.pos_10,
b.tax = v.pos_11;
/*
other sql statements are commented out
*/
END;
I got the same error:
Error(23,9): PLS-00103: Encountered the symbol "INTO" when expecting one of the following: constant exception table long double ref char time timestamp interval date binary national character nchar
line 23 refers to the word "INTO"
When I ran the statement not as part of str. proc:
I got a different error:
Error at Command Line:9 Column:8
Error report:
SQL Error: ORA-38104: Columns referenced in the ON Clause cannot be updated: "B"."LINE_2"
38104. 00000 - "Columns referenced in the ON Clause cannot be updated: %s"
*Cause: LHS of UPDATE SET contains the columns referenced in the ON Clause
*Action:
I have tried looking for answer since 2 days ago with no result, is there anyone can help how to solve this problem?
As I explained in the question referenced above, 'Columns referenced in the ON Clause cannot be updated' means that we cannot update columns in the MATCHED clause if they are included in the ON criteria.
You are in this position because you are trying to use MERGE to do something which it is not intended to do. Now you say
"I just need to run a simple update statement using Oracle Merge
statement because my SQL developer won't take it any other way."
Fnord. There is absolutely no way SQL Developer will prevent you running an update statement provided you get the syntax right. Unfortunately Oracle doesn't support the ANSI join syntax in DML statements (unlike other flovours of RDBMS), and the Oracle syntax is not intuitive:
update
(
select v.id
, v.xseq
, v.line_1
, v.line_2
, nvl(v.zip, '') AS pos_10
, nvl(v.tax, '') AS pos_11
from addr b
,temp_addr v
where ( b.id = v.id
and b.xseq = v.xseq )
and ( b.line_1 <> v.line_1
or b.line_2 <> v.line_2
or b.zip <> v.pos_10)
)
set b.line_1 = v.line_1,
b.line_2 = v.line_2,
b.zip = v.pos_10,
b.tax = v.pos_11
;
The following MERGE statement should at least compile and execute properly, although I'll agree with #APC that the right thing to do here is to use an UPDATE (because that's what you're really trying to do):
MERGE INTO ADDR b
USING TEMP_ADDR v
ON (b.id = v.id AND
b.xseq = v.xseq)
WHEN MATCHED THEN
UPDATE
SET b.line_1 = v.line_1,
b.line_2 = v.line_2,
b.zip = v.zip,
b.tax = v.tax;
I elminated the NVL manipulations you were doing because in Oracle a zero-length string (e.g. '') is the same as NULL, so the NVL calls were equivalent to saying "If this field is not NULL, return it - otherwise return NULL" which is sort of pointless.
Best of luck.

Resources