I'm recieving the ORA-01403 no data found error when trying to insert in a table after creating the following trigger:
CREATE OR REPLACE TRIGGER unic_disc
BEFORE insert ON disciplina
FOR EACH ROW
DECLARE
CURSOR cursor_professor IS
SELECT matricula_professor
FROM disciplina;
temp_prof disciplina.matricula_professor%type;
BEGIN
OPEN cursor_professor;
FETCH cursor_professor INTO temp_prof;
CLOSE cursor_professor;
END;
/
(the variables are in portugues, but their name does not interfere in the logic.)
the table creation,
CREATE TABLE disciplina (
codigo_disciplina NUMBER,
ementa VARCHAR2(50) NOT NULL,
conteudo_programatico VARCHAR2(100) NOT NULL,
matricula_professor NUMBER NOT NULL,
CONSTRAINT disciplina_pk PRIMARY KEY (codigo_disciplina),
CONSTRAINT disciplina_matricula_prof_fk FOREIGN KEY (matricula_professor) REFERENCES professor (matricula_professor)
);
My insert query:
INSERT INTO disciplina (codigo_disciplina,ementa,conteudo_programatico,matricula_professor) VALUES (7,'E6', 'C6',7777);
EDIT: I think the error is because I am selecting from the same table I'm editing.
I just needed to add PRAGMA_AUTONOMOUS_TRANSACTION after the DECLARE AND IT WORKED. I couldn't find it on my own, a friend told me.
Yes you are using a trigger (before insert) initially the table does not have any data and
the trigger is fired before insert when you are trying to insert data in the table, it
returns you with no records found . if you specify what is your requirement exactly i will
try to help you with the code .
Related
I need to make the below amendment to this stored procedure
create or replace PROCEDURE "USP_IMPORT_FOBTPP_DATA"
AS
BEGIN
INSERT INTO FINIMP.FOBT_PARTPAYMENT
SELECT
PART_PAYMENT_ID,
ISSUING_SHOP,
TILL_NUMBER,
SLIP_NUMBER,
FOBT_NUMBER,
WHO_PAID,
WHEN_PAID,
AMOUNT_LEFT_TO_PAY,
FOBT_VALUE,
STATUS
FROM IMPORTDB.CLN_FOBTPP;
COMMIT;
END;
In order to skip any records that would result in a primary key violation, this is so the dataload process does not break.
Source Table
CREATE TABLE "FINIMP"."FOBT_PARTPAYMENT"
( "PART_PAYMENT_ID" NUMBER(*,0),
"ISSUING_SHOP" CHAR(4 BYTE),
"TILL_NUMBER" NUMBER(3,0),
"SLIP_NUMBER" NUMBER(*,0),
"FOBT_NUMBER" VARCHAR2(30 BYTE),
"WHO_PAID" CHAR(20 BYTE),
"WHEN_PAID" DATE,
"AMOUNT_LEFT_TO_PAY" NUMBER(19,4),
"FOBT_VALUE" NUMBER(19,4),
"STATUS" CHAR(2 BYTE)
);
ALTER TABLE "FINIMP"."FOBT_PARTPAYMENT" ADD CONSTRAINT "PK_FOBT_PP" PRIMARY KEY ("PART_PAYMENT_ID", "ISSUING_SHOP", "WHEN_PAID")
I am new to PL/SQL, how can I do this?
There are a number of ways to accomplish this, and the best method depends on your environment/requirements. Is the CLN_FOBTPP table considerably large? Is the USP_IMPORT_FOBTPP_DATA procedure called frequently, and does it need to meet certain performance criteria? These are all things you should consider.
One way to do this would be to start with the query that you use.
create or replace PROCEDURE "USP_IMPORT_FOBTPP_DATA"
AS
BEGIN
INSERT INTO FINIMP.FOBT_PARTPAYMENT
SELECT ...
FROM IMPORTDB.CLN_FOBTPP;
This will return all of the rows of data from IMPORTDB.CLN_FOBTP and insert them into FINIMP.FOBT_PARTPAYMENT. Instead, you could control for this by doing:
INSERT INTO FINIMP.FOBT_PARTPAYMENT
SELECT ...
FROM IMPORTDB.CLN_FOBTPP WHERE PART_PAYMENT_ID NOT IN (FINIMP.FOBT_PARTPAYMENT)
This would go through the FOBT_PARTPAYMENT table and check to see if a row's PART_PAYMENT_ID existed in the table before doing the insert. However, this can be prohibitively expensive if the table is large or if you have performance requirements.
Another way would be to create a temp table for each time the procedure is called, store the values in that temp table, and then add the new rows after validating the data. This would look something like:
create global temporary table temp_USP_table ("PART_PAYMENT_ID" NUMBER(*,0), "ISSUING_SHOP" CHAR(4 BYTE),...) on commit delete rows;
create or replace PROCEDURE "USP_IMPORT_FOBTPP_DATA"
AS
BEGIN
INSERT INTO temp_USP_table
SELECT ...
FROM IMPORTDB.CLN_FOBTPP;
From there, you can do a number of things. You could use the same procedure to add the new rows from the temp table into the FINIMP.FOBT_PARTPAYMENT table:
delete from temp_USP_table where PART_PAYMENT_ID in FINIMP.FOBT_PARTPAYMENT;
insert into FINIMP.FOBT_PARTPAYMENT select * from temp_USP_table;
Or you could create a new procedure to load the new data from the temp_USP_table into the FINIMP.FOBT_PARTPAYMENT table, in case you'd like to do something additional to the new data before it's added to the table. Since you reference a data load, I would recommend going the temporary table route because it should allow you to load the data without issue. Once the data is loaded, you can worry about adding it to the proper table(s).
I am working with Oracle 11g and I got a question.
What I want to do is :
make a procedure
when this procedure has called, it will move data form one table to the other (column is almost same, but has different primary key)
Since, they do not use same field as PK, it occurs an error if use insert statement only.
So I want to do if table has a key than update, otherwise insert.
i.e. DDL like follow. It has almost same, but pk.
create table Tbl_A (
a_pk number constraints pk_tbl_a primary key
, b_pk number
, some_text varchar2(10)
, created date
, changed date
);
create table Tbl_B (
a_pk number
, b_pk number constraints pk_tbl_b primary key
, some_text varchar2(10)
, created date
, changed date
);
Psuedo of what I want :
create or replace procedure mv_data
is
begin
case when [if Tbl_B has same b_pk] then [update statement] end
else [create statement] end;
commit;
end;
I know I can not use case when like above, but that what I am trying to achieve is something like that. MyBatis could be a solution, but the client want to this with only DB.(Actually, this job will be executed by Oracle DBMS_SCHEDULE)
Thanks for your kind answers :D
As an alternative to the MERGE solution as suggested by valentin you can use the following construction:
begin
-- try the insert
insert...
exception
when dup_val_on_index
then
update...
end;
You want to use MERGE
Some references:
https://oracle-base.com/articles/9i/merge-statement
http://psoug.org/reference/merge.html
I have two tables (master-detail) I use to record orders, I need to create a trigger that allows me to update the "TOTAL_GENERAL" field that is in the master table with the sum of subtotals in the "SUBTOTAL" field the detail table that are related to the foreign key "ID_ORDEN" but I get an error with the trigger.
tables:
CREATE TABLE "ENCABEZADO_ORDEN"
("ID_ENCABEZADO" NUMBER(10,0),
"NUMERO_ORDEN" NUMBER(10,0),
"FECHA" DATE,
"NOMBRE_CLIENTE" VARCHAR2(50),
"DIRECCION" VARCHAR2(50),
"TOTAL_GENERAL" NUMBER(10,0),
"LUGAR_VENTA" VARCHAR2(50),
CONSTRAINT "ENCABEZADO_ORDEN_PK" PRIMARY KEY ("ID_ENCABEZADO")
USING INDEX ENABLE
)
CREATE TABLE "DETALLE_ORDEN"
("ID_DETALLE" NUMBER(10,0),
"PRODUCTO" VARCHAR2(50),
"PRECIO_UNITARIO" NUMBER(10,2),
"CANTIDAD" NUMBER(10,0),
"SUBTOTAL" NUMBER(10,2),
"ID_ENCABEZADO" NUMBER(10,0),
CONSTRAINT "DETALLE_ORDEN_PK" PRIMARY KEY ("ID_DETALLE")
USING INDEX ENABLE
)
/
ALTER TABLE "DETALLE_ORDEN" ADD CONSTRAINT "DETALLE_ORDEN_FK" FOREIGN KEY ("ID_ENCABEZADO")
REFERENCES "ENCABEZADO_ORDEN" ("ID_ENCABEZADO") ENABLE
/
trigger:
create or replace TRIGGER "CALCULAR_TOTAL_GENERAL"
BEFORE INSERT OR UPDATE ON "DETALLE_ORDEN"
FOR EACH ROW
DECLARE
V_ID_ENCABEZADO NUMBER(10,0);
BEGIN
SELECT "ID_ENCABEZADO"
INTO V_ID_ENCABEZADO
FROM "ENCABEZADO_ORDEN"
WHERE "ID_ENCABEZADO" = :NEW."ID_ENCABEZADO";
UPDATE "ENCABEZADO_ORDEN"
SET "TOTAL_GENERAL" = (SELECT SUM("SUBTOTAL") FROM "DETALLE_ORDEN"
WHERE "ID_ENCABEZADO" = V_ID_ENCABEZADO)
WHERE "ID_ENCABEZADO" = V_ID_ENCABEZADO;
END;
This is the error message I get when I insert or update the table "DETALLE_ORDEN":
1 error has occurred
ORA-04091: table CARLOSM.DETALLE_ORDEN is mutating, trigger/function may not see it
ORA-06512: at "CARLOSM.CALCULAR_TOTAL_GENERAL", line 9
ORA-04088: error during execution of trigger 'CARLOSM.CALCULAR_TOTAL_GENERAL'
Don't use triggers for this kind of logic (for that matter, don't use triggers ever; there's almost always a better way). Also, avoid storing redundant information in base tables whenever possible.
Far better design, with minimal impact to existing code is to
1) rename table "ENCABEZADO_ORDEN" (i.e. to "ENCABEZADO_ORDEN_TAB") and 2) disable/drop "TOTAL_GENERAL" field, and then 3) create a view with original name "ENCABEZADO_ORDEN" as:
CREATE OR REPLACE VIEW ENCABEZADO_ORDEN AS
SELECT O.*, (SELECT SUM(D.SUBTOTAL) FROM DETALLE_ORDEN D
WHERE D.ID_ENCABEZADO = O.ID_ENCABEZADO) TOTAL_GENERAL
FROM ENCABEZADO_ORDEN_TAB O;
This will ensure TOTAL_GENERAL is always correct (in fact, any efforts to set it directly to some other value via update of ENCABEZADO_ORDEN will result in immediate syntax error).
If performance is an issue (i.e. users frequently query TOTAL_GENERAL field in ENCABEZADO_ORDEN table for orders with large numbers of detail records in DETALLE_ORDEN, causing Oracle to repeatedly fetch&sum multitudes of SUBTOTALS) then use a materialized view instead of a basic view.
I am having problems with this code below, which is a trigger used in Oracle SQL:
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
BEGIN
IF :new.tutorName = :old.tutorName
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
This trigger is used to prevent users from entering the same tutor name at different records.
After I insert two records with the same tutorname, the trigger does not block me from inserting it. Is there anyone can tell me what are the problems with this coding? Here are the sample format and insert values:
INSERT INTO tutors VALUES (tutorID, tutorName tutorPhone, tutorAddress, tutorRoom, loginID);
INSERT INTO tutors VALUES ('13SAS01273', 'Tian Wei Hao', '019-8611123','No91, Jalan Wangsa Mega 2, 53100 KL', 'A302', 'TianWH');
Trigger in Kamil's example will throw ORA-04091, you can see this with your own eyes here. ROLLBACK in a trigger is unnecessary, it runs implicitly when a trigger makes a statement to fail.
You can prohibit any DML on table by altering it with read only clause:
alter table tutors read only;
At last, integrity should be declarated with integrity constraints and not with triggers.
Good luck!
You don't need a trigger for this in Oracle.
You can do it with an "unique index" on the tutorName column (see http://docs.oracle.com/cd/B28359_01/server.111/b28310/indexes003.htm#i1106547).
Note: about your trigger, it fails on checking for another record with the same tutorName because it's not scanning the tutors table for another record with the same tutorName, it's just comparing the tutorName values of the row you are creating (in this case, old.tutorName is just NULL, because the row doesn't exist yet).
Check the case in yours trigger body
IF :new.tutorName = :old.tutorName
It returns true only if 'tutorName' value is the same in new and old record. When you'll trying to updat some value you'll get
IF 'someTutorName' = 'someTutorName'
which will return TRUE.
Inserting row cannot fire this rule because you're trying to compare something like that:
'someTutorName' = NULL
This case always returns FALSE.
Try to use something like that
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
rowsCount INTEGER;
BEGIN
SELECT COUNT(*) FROM tutors WHERE tutorName is :new.tutorName INTO rowsCount;
IF rowsCount > 0
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
But the best solution is the one mentioned by friol - use unique index by executing SQL like this
ALTER TABLE tutors
ADD CONSTRAINT UNIQUE_TUTOR_NAME UNIQUE (tutorName);
If you wanna completely ignore recording a row to a table you can follow these steps
rename table to something else and create a view with the same name and create an instead of trigger.
create table usermessages (id number(10) not null)
GO
alter table usermessages rename to xusermessages
GO
create or replace view usermessages as (select * from xusermessages)
GO
create or replace trigger usermessages_instead_of_trg
instead of insert or update on usermessages
for each row
begin
Null ;
end ;
GO
insert into usermessages(123)
Live test available here below
http://sqlfiddle.com/#!4/ad6bc/2
I am trying to create a trigger which will enter values into a table terminated_employees when we delete values from the nm_employees table. I have written the trigger but it does not work. Is my trigger format right? Any ideas?
CREATE TABLE nm_departments(
dept2 varchar(20),
CONSTRAINT empPK PRIMARY KEY (dept2)
);
CREATE TABLE nm_employees(
name varchar(20),
dept varchar(20),
CONSTRAINT departments FOREIGN KEY (dept) REFERENCES nm_departments (dept2)ON DELETE CASCADE
);
CREATE TABLE terminated_employees(
te_name varchar(20),
te_dept varchar(20)
);
CREATE TRIGGER term_employee AFTER DELETE ON nm_employee
FOR EACH ROW
BEGIN
INSERT INTO terminated_employees (NEW.te_name, NEW.te_dept) VALUES (OLD.name,OLD.dept)
END;
You should not be specifying the NEW. on the column names of your INSERT statement. These are the columns in the terminated_employees table, NOT the new values. i.e.
INSERT INTO terminated_employees (te_name, te_dept)
VALUES (OLD.name,OLD.dept)
You can use show errors (or show err) in SQL*Plus to see the exact error.
You have a number of problems:
Wrong table name on create trigger (missing the s)
Missing ; after instert statement
The OLD. need to have : prefix. i.e. :OLD.name