SQL XPath with upper case INSERT fails with syntax error - xpath

I have a SQL Server query to insert an XML node into some XML; when I use an upper case INSERT it gives a syntax error but lower case insert works.
UPDATE #xTABLE
SET x.modify('
INSERT <newthing>I am here</newthing>
as last
into (/rootthing/InsertInMe)[1]
');
Why does this fail with a syntax error given the examples have upper case here:
Msg 2209, Level 16, State 1, Line 25
XQuery [#xTABLE.x.modify()]: Syntax error near 'I'
Running this in SSMS.
As a reference this has upper case in the examples: https://learn.microsoft.com/en-us/sql/t-sql/xml/insert-xml-dml?view=sql-server-ver15
Here is some sample SQL with the working (first one) and failing (second one) UPDATE:
-- setup some sample data:
DECLARE #xtable AS TABLE (
x xml
);
DECLARE #x AS XML;
SET #x = '
<rootthing>
<InsertInMe>
</InsertInMe>
</rootthing>
';
INSERT INTO #xtable (x) Values(#x);
SELECT x AS xBefore
FROM #xtable;
-- this works
UPDATE #xTABLE
SET x.modify('
insert <newthing>I am here</newthing>
as last
into (/rootthing/InsertInMe)[1]
');
-- this gives a syntax error
UPDATE #xTABLE
SET x.modify('
INSERT <newthing>I am here</newthing>
as last
into (/rootthing/InsertInMe)[1]
');
SELECT x AS xAfter
FROM #xtable;

XQuery is case sensitive. INSERT <> insert. This noted in XQuery's documentation (which SQL Server follows per XQuery Language Reference (SQL Server)) in section 2 "Basics".
Like XML, XQuery is a case-sensitive language. Keywords in XQuery use lower-case characters and are not reserved—that is, names in XQuery expressions are allowed to be the same as language keywords, except for certain unprefixed function-names listed in A.3 Reserved Function Names.
As a result, INSERT is not a recognised syntax, as it's not insert.

Related

Missing parenthesis in oracle sql developer

Hello so i was coding a procedure for my database but i got stuck when i the compiler(Oracle SQL Developer) started telling me that
there is a missing right parenthesis
but there is no missing parenthesis as much as I observe.
Here is the procedure:
CREATE OR REPLACE PROCEDURE generer_listes_preferees (
p_id_user IN UTILISATEUR."id_user"%TYPE,
p_films OUT SYS_REFCURSOR
)
AS
-- Déclarer les variables locales
CURSOR c_films IS
SELECT *
FROM OBJET
WHERE "categorie_obj" LIKE 'Films' AND "id_objet" IN (
SELECT "id_objet"
FROM AVIS
WHERE "id_user" = p_id_user
ORDER BY "note" DESC, "date_avis" DESC
)
FETCH FIRST 10 ROWS ONLY;
BEGIN
-- Générer la liste de films préférés
OPEN p_films FOR SELECT * FROM TABLE(c_films);
END;
What could be the issue?
The procedure does not compile because the query is not valid. In particular, Oracle is looking for the closing parenthesis to finish the subquery, but it finds an unexpected ORDER BY clause instead:
SELECT *
FROM OBJET
WHERE "categorie_obj" LIKE 'Films' AND "id_objet" IN (
SELECT "id_objet"
FROM AVIS
WHERE "id_user" = p_id_user
ORDER BY "note" DESC, "date_avis" DESC
)
(Demo)
The error message could indeed be more useful, but the problem with incorrect code is that the parser can't really figure out your intentions.
Even if the ORDER BY clause was allowed, it wouldn't accomplish anything. The IN () operator is not sensitive to order. I guess you meant to order the main outer query.
It's also worth noting that "categorie_obj" LIKE 'Films' is the same as "categorie_obj" = 'Films'. Performance overhead (it has to parse the expression looking for wildcards) is for sure negligible, but I think it's more readable to just be explicit.
On a side note, I'd advise against double quoting identifiers. The only feature it provides is to hard-code the table/column case, so you're forced to type the quotes and the exact case every time since expressions WHERE categorie_obj or WHERE CATEGORIE_OBJ will no longer work.

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.

Can I use a lexical parameter in a FOR loop select statement in Oracle

I am trying to use a lexical parameter in a FROM and WHERE clause in a FOR loop. Lexical parameters get populated at run time. I get a compilation error when I try to run it in TOAD:
FOR r_inv IN (SELECT i.SERIAL_NO,i.HDD_MODEL,i.HDD_SN FROM inventory i &LP_from)
LOOP ...
Can I even do this? I know that you cannot use lexical parameters with PL/SQL code, but here I am actually trying to use it with SQL statement inside of the PL/SQL code.
The error I am getting is:
Found '&LP_from', Expecting: * + - : CASE CONNECT_BY_ROOT CONTINUE CURSOR decimal number EXECUTE FALSE float FORALL integer MERGE MOD MULTISET NEW NULL PRIOR REM SQL string THE TRUE -or- ( identifier REVERSE

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