Creating trigger with 'instead of delete' - oracle

I want to treat a delete statement as an update but it throws this error, and I don't know what it means.
create or replace trigger Miembros_V_IOD
instead of delete on Miembros_V
for each row
Begin
update Miembros set (end_date = sysdate)
where Miembros.nick = :old.nick
and Miembros.club = :old.club;
end;
LINE/COL ERROR
-------- ----------------------------------------------------------
2/4 PL/SQL: SQL Statement ignored
2/34 PL/SQL: ORA-00907: missing right parenthesis

The error is pointing to the = sign. You're getting a PL/SQL error - albeit one caused by the inner SQL - and for a trigger the line numbers in PL/SQL errors start from the DECLARE (if you have one) or BEGIN, not from the start of the overall CREATE statement. So the 2/34 refers to character 34 of the second line of the PL/SQL part, which is:
update Miembros set (end_date = sysdate)
... which is the =.
You shouldn't have the parenthesis around (end_date = sysdate):
create or replace trigger Miembros_V_IOD
instead of delete on Miembros_V
for each row
begin
update Miembros set end_date = sysdate
where Miembros.nick = :old.nick
and Miembros.club = :old.club;
end;
/
View MIEMBROS_V created.
db<>fiddle
The syntax diagram in the documentation shows that parentheses can go around a list of columns on the left-had side of the equals sign, or around a subquery on the right; but not around the whole set clause. Because you have set (end_date it's expecting that to either have a comma or closing parenthesis next, i.e. set (end_date) = ... - hence the ORA-00907 being thrown when it didn't see that.

Related

The number specified in exact fetch is less than the rows returned: is that impossible

I have defined the following procedure in PL/SQL:
CREATE OR REPLACE PROCEDURE deleteFromStudyCase(studycase_id IN NUMBER)
IS
VAL_CARRIER_ID_GLOBAL NUMBER(19,0);
EST_OBJ_ID_GLOBAL NUMBER(19,0);
INVEST_TASK_ID_GLOBAL NUMBER(19,0);
BEGIN
-- Fill the variables
SELECT IT.ID into INVEST_TASK_ID_GLOBAL
FROM T_INVESTIGATIONTASK IT
WHERE IT.STUDYCASE_ID = studycase_id;
SELECT EO.ID into EST_OBJ_ID_GLOBAL
FROM T_ESTIMATIONOBJECT EO
WHERE EO.INVESTIGATIONTASK_ID = INVEST_TASK_ID_GLOBAL;
SELECT VC.ID into VAL_CARRIER_ID_GLOBAL
FROM T_VALIDATIONCARRIER VC
WHERE VC.IA_ESTIMATIONOBJECT_ID = EST_OBJ_ID_GLOBAL;
....many DELETE statements...
END deleteFromStudyCase;
When I try to use it like this:
BEGIN
DELETEFROMSTUDYCASE(30111);
END;
At runtime it fails with this error:
Error report -
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "DELETEFROMSTUDYCASE", line 9
ORA-06512: at line 2
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
Meaning that one of the selects is returning more than just one value as expected. I tried so to run the select statements separately to see which one was the problem but:
SELECT IT.ID
FROM T_INVESTIGATIONTASK IT
WHERE IT.STUDYCASE_ID = 30111;
is returning only 10053.
SELECT EO.ID
FROM T_ESTIMATIONOBJECT EO
WHERE EO.INVESTIGATIONTASK_ID = 10053;
is returning only 933.
SELECT VC.ID
FROM T_VALIDATIONCARRIER VC
WHERE VC.IA_ESTIMATIONOBJECT_ID = 933;
is returning only 12.
Since this part of my database has a tree structure I have also created the following procedure:
CREATE OR REPLACE PROCEDURE deleteFromInvestigationTask(invest_task_id IN NUMBER)
IS
VAL_CARRIER_ID_GLOBAL NUMBER(19,0);
EST_OBJ_ID_GLOBAL NUMBER(19,0);
BEGIN
-- Fill the variables
SELECT EO.ID into EST_OBJ_ID_GLOBAL
FROM T_ESTIMATIONOBJECT EO
WHERE EO.INVESTIGATIONTASK_ID = invest_task_id;
SELECT VC.ID into VAL_CARRIER_ID_GLOBAL
FROM T_VALIDATIONCARRIER VC
WHERE VC.IA_ESTIMATIONOBJECT_ID = EST_OBJ_ID_GLOBAL;
...many DELETE statements...
And this one works correctly.
So the other one should work, why do I continue to get that error? What else could it be?
I found the problem.
In the first procedure, the parameter name is studycase_id which is also the name of a column in the table T_INVESTIGATIONTASK so the WHERE condition IT.STUDYCASE_ID = studycase_id is wrong: that's why the first SELECT is failing. I changed the parameter name in study_case_id and solved my problem.

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.

PL/SQL invalid identifier, table_or_view doesn't exist, column not allowed

i'm currently writing a school assignment but got stuck. Unfortunately I have no reference material to browse through and oracle's user manuals are anything but friendly.
The assignment is as followed: "Create a trigger on table sms_budget_abonnement with help of a stored procedure which shows the bill in a new table when a user has passed the max_number of texts to be send within his/hers subscription
these are the relevant tables (<>English translation):
KLANTGEGEVENS <customer information>
NAAM <name> GESLACHT ADRES WOONPLAATS MOBIEL_NUMMER REKENINGNUMMER <billing_number> ABONNEMENTSVORM <subscription type> START_DATUM EIND_DATUM
SMS-BERICHT <text_message>
MOBIELE_NUMMER_VERZENDER <mobile_number_sender> MOBIELE_NUMMER_ONTVANGER LENGTE DATUM TIJD INHOUD (GEDOCEERD)
ABONNEMENTSVORMEN <subscription_type>
NAAM <subscription_name> BASISKOSTEN SMS <max_number_of_text_messages> SMS-BOETE <price per extra message> BELLEN
FACTUUR <billing sheet>
REKENINGNUMMER <billing_number> DATE BEDRAG <price> STATUS
This is the code/query I have written:
CREATE OR REPLACE TRIGGER opdracht_2e
BEFORE INSERT ON SMSBERICHT
FOR EACH ROW
DECLARE
NR_OF_PERSON NUMBER;
NR_OF_TEXT_MESSAGES NUMBER;
MAX_NUMBER_SMS NUMBER;
REKENING_NUMMER VARCHAR(255);
BOETE NUMBER;
BEGIN
select DISTINCT MOBIELNUMMERVERZENDER INTO NR_OF_PERSON
FROM SMSBERICHT;
SELECT COUNT(*) INTO NR_OF_TEXT_MESSAGES
FROM SMSBERICHT
WHERE MOBIELNUMMERVERZENDER = NR_OF_PERSON;
SELECT SMS into MAX_NUMBER_SMS
FROM ABONNEMENTSVORMEN av
JOIN Klantgegevens k ON k.ABBONEMENTSVORM = av.naam
WHERE k.mobielnummmer = NR_OF_PERSON;
SELECT REKENINGNUMMER into REKENING_NUMMER
FROM Klantgegevens
WHERE mobielnummer = NR_OF_PERSON;
SELECT SMS_BOETE INTO BOETE
FROM ABBONEMENTSVORMEN av
JOIN Klantgegevens k on k.ABBONEMENTSVORM = av.naam
WHERE k.mobielnummer = NR_OF_PERSON;
IF(NR_OF_TEXT_MESSAGES > MAX_NUMBER_SMS) THEN
insert into factuur
values (REKENING_NUMMER, SYSDATE, (NR_OF_TEXT_MESSAGES - MAX_NUMBER_SMS) * BOETE, "TE BETALEN");
END IF;
END;
However I'm getting the following errors which don't really help me in the right direction, if anyone care's to clarify these messages to me that would be awesome.
Project: sqldev.temp:/IdeConnections%23hanze.jpr
hanze
Error(16,1): PL/SQL: SQL Statement ignored
Error(19,7): PL/SQL: ORA-00904: "K"."MOBIELNUMMMER": invalid identifier
Error(25,1): PL/SQL: SQL Statement ignored
Error(26,6): PL/SQL: ORA-00942: table or view does not exist
Error(31,1): PL/SQL: SQL Statement ignored
Error(32,84): PL/SQL: ORA-00984: column not allowed here
Thanks in advance!
You have an extra m in line 19:
WHERE k.mobielnummmer = NR_OF_PERSON;
The second error is in this section, specifically the FROM lone:
SELECT SMS_BOETE INTO BOETE
FROM ABBONEMENTSVORMEN av
JOIN Klantgegevens k on k.ABBONEMENTSVORM = av.naam
WHERE k.mobielnummer = NR_OF_PERSON;
An earlier reference only has one B; ABBONEMENTSVORMEN. So you are using the wrong name. It doesn't help that the table descriptions you showed are not accurate. It would have better to show the actual definitions, e.g. from describe <table>.
You have a string value enclosed in double quotes instead of single quotes inthis line:
values (REKENING_NUMMER, SYSDATE, (NR_OF_TEXT_MESSAGES - MAX_NUMBER_SMS) * BOETE, "TE BETALEN");
It should be 'TE BETALEN'. Double-quoted or unquoted strings are interpreted as identifiers, in this context a column name.
All pretty simple mistakes that were fairly clearly indicated by the error messages.

Error in Oracle procedure, object is invalid

Where is the bug? Compilation ends with errors and I have no idea where I'm going wrong.
create or replace
PROCEDURE make_payoff(user_id_argument number)
is
begin
payoff_amount:= 0;
CURSOR Clicks IS
SELECT c.cpc FROM click as c JOIN widget w ON w.id = c.widget_id JOIN website web ON web.id = w.website_id WHERE web.user_id = user_id_argument AND c.payoff_id IS NULL;
BEGIN
FOR Click IN Clicks
LOOP
payoff_amount:= payoff_amount + Click.cpc;
END LOOP;
INSERT INTO payoff (user_id, amount) VALUES (user_id_argument, payoff_amount);
COMMIT;
end;
I'm getting:
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00905: object S10306.MAKE_PAYOFF is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
EDIT:
I've fixed Cursor name but error is the same
This is the error you get when you try to use the procedure. Not the error you get when you compile it. You need to find the error you get when you compile the procedure, probably using show errors and attempt to solve that problem.
Your problem is that for click in click should be for click in clicks... Not the extra s, so you are looping through the cursor.
Additionally, in your cursor you've written FROM click as c, which is not valid in Oracle. This should be FROM click c
And two BEGINs... remove the first one.
Alex Poole has noted that you've also not declared the type of the variable payoff_amount. You should declare this as a number:
payoff_amount number := 0;
However, there is no need to do this, no need to loop, no need to use a procedure at all. This is possible with a single SQL statement:
insert into payoff (user_id, amount)
select 'user_id_argument', sum(c.cpc)
from click c
join widget w
on w.id = c.widget_id
join website web
on web.id = w.website_id
where web.user_id = user_id_argument
and c.payoff_id IS NULL;
You missed s.
FOR Click IN Clicks
LOOP
payoff_amount:= payoff_amount + Click.cpc;
END LOOP;
Anyway, don't name variables and cursors so close to database fields. Add some prefix, for example, to differentiate easily.
I created a procedure for my user under the user sys. When starting the procedure, I received the same errors as yours. The procedure was valid. Recompiling the object under different users did not help solve the problem. I drop the procedure and created it as my user. Problem solved.

Resources