Cannot insert data in trigger - oracle11g

It give me error example image at below:
Trigger code:
CREATE OR REPLACE TRIGGER InsertNewStaffs
BEFORE INSERT ON Staffs
FOR EACH ROW
ENABLE
DECLARE
v_user varchar(255);
v_date varchar(255);
v_Staffs_ID Staffs.Staffs_ID%TYPE;
v_Staffs_Name Staffs.Staffs_Name%TYPE;
v_Staffs_Contact_Number Staffs.Staffs_Contact_Number%TYPE;
v_Staffs_Email Staffs.Staffs_Email%TYPE;
v_Orders_ID Staffs.Orders_ID%TYPE;
v_count INTEGER;
BEGIN
SELECT count(*) INTO v_count FROM Staffs
WHERE Staffs_ID = v_Staffs_ID OR
Staffs_Name = v_Staffs_Name OR
Staffs_Contact_Number = v_Staffs_Contact_Number OR
Staffs_Email = v_Staffs_Email;
IF v_count > 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Oops, some data is already exists. Please try again...');
DBMS_OUTPUT.PUT_LINE('Oops, some data is already exists. Please try again...');
SELECT user, TO_CHAR(sysdate, 'DD/MON/YYYY HH24:MI:SS') INTO v_user, v_date FROM dual;
ELSE
INSERT INTO Staffs(Staffs_ID, Staffs_Name, Staffs_Contact_Number, Staffs_Email, Orders_ID)
VALUES(v_Staffs_ID, v_Staffs_Name, v_Staffs_Contact_Number, v_Staffs_Email, v_Orders_ID);
DBMS_OUTPUT.PUT_LINE('One Row Inserted By ' || v_user || CHR(10));
DBMS_OUTPUT.PUT_LINE('Inserted data at ' || v_date);
INSERT INTO monitorInsertStaffs(user_name, entry_date, operation)
VALUES(v_user, v_date, 'Insert');
END IF;
END;
/
My Table:
CREATE TABLE Staffs(
Staffs_ID char(20) NOT NULL,
Staffs_Name varchar(255) NOT NULL,
Staffs_Contact_Number varchar(50) NOT NULL,
Staffs_Email varchar(255) NOT NULL,
Orders_ID char(20),
PRIMARY KEY (Staffs_ID),
FOREIGN KEY (Orders_ID) REFERENCES Orders(Orders_ID)
);
CREATE TABLE Orders(
Orders_ID char(20) NOT NULL,
Order_Date DATE NOT NULL,
Order_Status varchar(255) NOT NULL,
Order_Quantity int NOT NULL,
Order_TotalAmount NUMERIC(10,2) NOT NULL,
Order_TotalPrice NUMERIC(10,2) NOT NULL,
PRIMARY KEY (Orders_ID),
Pets_Products_ID char(20),
CustomerID char(20),
FOREIGN KEY (Pets_Products_ID) REFERENCES Pets_Products(Pets_Products_ID),
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
I try to insert data and if the data has existed it will show RAISE_APPLICATION_ERROR(-20000, 'Oops, some data is already exists. Please try again...'); but it didn't show the message and also cannot insert data when no exists the data.
I don't know where is error code that I find.

The whole concept is just wrong.
you've based trigger on a table into which you're just inserting a row (staffs)
then you're selecting from the same table (it'll raise the mutating table error if you try to insert more than a single row)
the where clause uses local variables that have no values
insert into staffs cause the same trigger to fire over and over again, until Oracle concludes that that's enough and raises the error

Don't use a trigger. Use UNIQUE constraints:
CREATE TABLE Staffs(
Staffs_ID char(20) NOT NULL,
Staffs_Name varchar(255) NOT NULL,
Staffs_Contact_Number varchar(50) NOT NULL,
Staffs_Email varchar(255) NOT NULL,
Orders_ID char(20),
PRIMARY KEY (Staffs_ID),
UNIQUE (Staffs_Name),
UNIQUE (Staffs_Contact_Number),
UNIQUE (Staffs_Email),
FOREIGN KEY (Orders_ID) REFERENCES Orders(Orders_ID)
);
(However, you should also consider whether your business requirements make sense or if you can have multiple staff members called Jane Smith or if you can have two staff members who share an office with the same telephone number?)
If you want to use a logging table then use an autonomous transaction to just insert into that table:
CREATE OR REPLACE TRIGGER InsertNewStaffs
BEFORE INSERT ON Staffs
FOR EACH ROW
ENABLE
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO monitorInsertStaffs(
user_name, entry_date, operation
) VALUES(
:NEW.Staffs_ID, SYSDATE, 'Insert'
);
COMMIT;
END;
/
db<>fiddle here

Related

Creating trigger to compare dates from two tables [duplicate]

This question already has an answer here:
Checking if date in table B is between date in Table A before inserting SQLite
(1 answer)
Closed 1 year ago.
I have two tables that both have separate start/end date values in them. One Project can contain many Plans, and a specific Plan start/end date should be between its Project start/end date. I dont know how to validate this, and I have tried to use triggers but I just cant figure it out. Can someone give me some pointers on what I'm doing wrong?
This is my trigger:
%%sql
CREATE TRIGGER beforeInsertInPlan BEFORE INSERT ON Plan FOR EACH ROW
BEGIN
SELECT proID.projectID FROM Project
INNER JOIN Project ON pID = Project.projectID
WHERE
And here are my two tables:
%%sql
DROP TABLE IF EXISTS Project;
CREATE TABLE Project (
projectID varchar(255) NOT NULL UNIQUE,
name varchar(255) NOT NULL DEFAULT ' ',
leader varchar(255) NOT NULL DEFAULT ' ',
budget varchar(255) NOT NULL DEFAULT '0',
startDate DATE NOT NULL DEFAULT '2000-12-31',
endDate DATE NOT NULL DEFAULT '2000-12-31'
CHECK (JulianDay(startDate) <= JulianDay(endDate)),
PRIMARY KEY (projectID)
);
and:
%%sql
DROP TABLE IF EXISTS Plan;
CREATE TABLE Plan (
pID varchar(255) NOT NULL UNIQUE,
projectID varchar(255) DEFAULT NULL,
name varchar(255) NOT NULL DEFAULT ' ',
startDate DATE NOT NULL DEFAULT ' ',
endDate DARE NOT NULL DEFAULT ' '
CHECK (JulianDay(startDate) <= JulianDay(endDate) AND (startDate >= Project.startDate) AND
(endDate <= Project.endDate)),
PRIMARY KEY (pID, projectID),
FOREIGN KEY (projectID) REFERENCES Project(projectID)
);
First a BEFORE INSERT trigger will probably result in nothing but issues. see https://sqlite.org/lang_createtrigger.html#cautions_on_the_use_of_before_triggers
So here's a trigger that I believe will work as intended albeit it deleting the inserted row:-
CREATE TRIGGER IF NOT EXISTS afterInsertInPlan
AFTER INSERT ON plan
WHEN (
(NOT (new.startdate) BETWEEN
(SELECT startdate FROM project WHERE projectID = new.projectID)
AND
(SELECT enddate FROM project WHERE projectID = new.projectID)
)
OR
(NOT (new.enddate) BETWEEN
(SELECT startdate FROM project WHERE projectID = new.projectID)
AND
(SELECT enddate FROM project WHERE projectID = new.projectID)
)
)
BEGIN
DELETE FROM plan WHERE pID = new.pID ;
END
;
Testing/Demo
The above was tested using :-
DROP TABLE IF EXISTS Project;
CREATE TABLE Project (
projectID varchar(255) NOT NULL UNIQUE,
name varchar(255) NOT NULL DEFAULT ' ',
leader varchar(255) NOT NULL DEFAULT ' ',
budget varchar(255) NOT NULL DEFAULT '0',
startDate DATE NOT NULL DEFAULT '2000-12-31',
endDate DATE NOT NULL DEFAULT '2000-12-31'
CHECK (JulianDay(startDate) <= JulianDay(endDate)),
PRIMARY KEY (projectID)
);
DROP TABLE IF EXISTS Plan;
CREATE TABLE Plan (
pID varchar(255) NOT NULL UNIQUE,
projectID varchar(255) DEFAULT NULL,
name varchar(255) NOT NULL DEFAULT ' ',
startDate DATE NOT NULL DEFAULT ' ' ,
endDate DATE NOT NULL DEFAULT ' ',
CHECK (endDate >= startDate),
PRIMARY KEY (pID, projectID),
FOREIGN KEY (projectID) REFERENCES Project(projectID)
);
DROP TABLE IF EXISTS trigger_log;
CREATE TABLE IF NOT EXISTS trigger_log (id INTEGER PRIMARY KEY, timestamp TEXT DEFAULT CURRENT_TIMESTAMP, trigger_text TEXT);
DROP TRIGGER IF EXISTS beforeInsertInPlan;
CREATE TRIGGER IF NOT EXISTS afterInsertInPlan
AFTER INSERT ON plan
WHEN (
(NOT (new.startdate) BETWEEN
(SELECT startdate FROM project WHERE projectID = new.projectID)
AND
(SELECT enddate FROM project WHERE projectID = new.projectID)
)
OR
(NOT (new.enddate) BETWEEN
(SELECT startdate FROM project WHERE projectID = new.projectID)
AND
(SELECT enddate FROM project WHERE projectID = new.projectID)
)
)
BEGIN
DELETE FROM plan WHERE pID = new.pID ;
INSERT INTO trigger_log (trigger_text) VALUES('DELETED FROM Plan Table due to date(s) not within project. pID was '||new.pID);
END
;
INSERT INTO project VALUES ('P1','P1','Mary',100,'2021-09-01','2022-09-30');
INSERT INTO plan VALUES ('P1P1','P1','Plan1','2021-09-01','2022-09-30');
INSERT INTO plan VALUES ('P1P2','P1','Plan2','2021-08-01','2022-09-30'); /* X */
INSERT INTO plan VALUES ('P1P3','P1','Plan3','2021-09-01','2022-10-30'); /* X */
INSERT INTO plan VALUES ('P1P4','P1','Plan4','2020-09-01','2022-10-30'); /* X */
INSERT INTO plan VALUES ('P1P5','P1','Plan5','2021-09-01','2021-10-01');
INSERT INTO plan VALUES ('P1P6','P1','Plan6','2021-10-01','2021-11-01');
INSERT INTO plan VALUES ('P1P7','P1','Plan7','2021-11-01','2021-12-01');
SELECT * FROM plan;
/* Cleanup Environment */
SELECT * FROM trigger_log;
DROP TABLE IF EXISTS trigger_log;
DROP TRIGGER IF EXISTS beforeInsertInPlan;
DROP TABLE IF EXISTS Plan;
DROP TABLE IF EXISTS Project;
When run then the results are :-
The Plan's in the plan table:-
i.e. those commented with an X (3) were not inserted
The trigger_log (used to confirm triggering when testing) :-
i.e. the 3 commented with an X that were not inserted have been logged accordingly.
Example of why not to use a BEFORE INSERT trigger
Swapping the trigger to use BEFORE INSERT and :-
All are inserted :-
None are deleted even though logged:-
i.e. nothing to delete as nothing has been inserted.

PL/SQL Ttrigger select data from 2 tables with the same row

i have a probelm with a little project.
I have these tables:
USER (TAG VARCHAR, NICKNAME VARCHAR, TAG_CLAN VARCHAR)
DONATION(DATE_DON DATE, DON_SEND VARCHAR, DON_REIC VARCHAR)
THE ELEMENTS: DON_SEND AND DON_REICV ARE THE FOREIGN KEY THAT POINTS TO THE MANI (TAG) OF THE TABLE USER.
I'm trying to make a trigger that allows users to make and receive donations only if the TAG_CLAN is the same
I tried in this way but it doesn't work:
CREATE OR REPLACE TRIGGER CONTROLLO_USER_DONAZIONE
BEFORE INSERT ON DONAZIONE
FOR EACH ROW
DECLARE
TAG_C1 UTENTE.NUM_CLAN%TYPE;
TAG_C2 UTENTE.NUM_CLAN%TYPE;
CLAN_DIFF EXCEPTION;
BEGIN
SELECT U.NUM_CLAN INTO TAG_C1
FROM DONAZIONE D JOIN UTENTE U ON D.COD_UTENTE_EFFETTUA=U.TAG_USER
WHERE D.COD_UTENTE_EFFETTUA=(:NEW.COD_UTENTE_EFFETTUA);
SELECT U.NUM_CLAN INTO TAG_C2
FROM DONAZIONE D JOIN UTENTE U ON D.COD_UTENTE_RICEVE=U.TAG_USER
WHERE D.COD_UTENTE_RICEVE=(:NEW.COD_UTENTE_RICEVE);
IF TAG_C1<>TAG_C2 THEN
RAISE CLAN_DIFF;
END IF;
EXCEPTION
WHEN CLAN_DIFF THEN
RAISE_APPLICATION_ERROR(-20003,'NON SIETE NELLO STESSO CLAN, QUINDI NON PUOI RICEVERE/DONARE CARTE!');
END;
Can you help me please?
There are some differences between the tables you posted and the code of your trigger. With tables like these:
CREATE TABLE utente
(
TAG_USER VARCHAR2(10) PRIMARY KEY,
NICKNAME VARCHAR2(10),
NUM_CLAN VARCHAR2(10)
);
CREATE TABLE DONAZIONE
(
DATE_DON DATE,
COD_UTENTE_EFFETTUA VARCHAR2(10) REFERENCES utente(TAG_USER),
COD_UTENTE_RICEVE VARCHAR2(10) REFERENCES utente(TAG_USER)
);
This could be your trigger:
CREATE OR REPLACE TRIGGER CONTROLLO_USER_DONAZIONE
BEFORE INSERT
ON DONAZIONE
FOR EACH ROW
DECLARE
TAG_C1 UTENTE.NUM_CLAN%TYPE;
TAG_C2 UTENTE.NUM_CLAN%TYPE;
CLAN_DIFF EXCEPTION;
BEGIN
SELECT U.NUM_CLAN
INTO TAG_C1
FROM UTENTE U
WHERE U.TAG_USER = :NEW.COD_UTENTE_EFFETTUA;
SELECT U.NUM_CLAN
INTO TAG_C2
FROM UTENTE U
WHERE U.TAG_USER = :NEW.COD_UTENTE_RICEVE;
IF TAG_C1 <> TAG_C2
THEN
RAISE CLAN_DIFF;
END IF;
EXCEPTION
WHEN CLAN_DIFF
THEN
RAISE_APPLICATION_ERROR(-20003, 'NON SIETE NELLO STESSO CLAN, QUINDI NON PUOI RICEVERE/DONARE CARTE!');
END;
With data like the following:
insert into utente(TAG_USER, NICKNAME, NUM_CLAN) values ('one', 'User one', 'Numbers');
insert into utente(TAG_USER, NICKNAME, NUM_CLAN) values ('two', 'User two', 'Numbers');
insert into utente(TAG_USER, NICKNAME, NUM_CLAN) values ('a', 'User a', 'Letters');
it works like:
SQL> insert into donazione(DATE_DON, COD_UTENTE_EFFETTUA, COD_UTENTE_RICEVE) values (sysdate, 'one', 'two');
1 row created.
SQL> insert into donazione(DATE_DON, COD_UTENTE_EFFETTUA, COD_UTENTE_RICEVE) values (sysdate, 'a', 'two');
insert into donazione(DATE_DON, COD_UTENTE_EFFETTUA, COD_UTENTE_RICEVE) values (sysdate, 'a', 'two')
*
ERROR at line 1:
ORA-20003: NON SIETE NELLO STESSO CLAN, QUINDI NON PUOI RICEVERE/DONARE CARTE!
ORA-06512: at "ALEK.CONTROLLO_USER_DONAZIONE", line 23
ORA-04088: error during execution of trigger 'ALEK.CONTROLLO_USER_DONAZIONE'
The issue in your code is that you do a join, assuming that the record you're inserting already exists in the table DONAZIONE, this getting a no_data_found exception.
As an aside, I changed varchar into varchar2; have a look at the difference between them.
Also, this assumes that NUM_CLAN always is NOT NULL.
EDIT:
Given the way you need to handle null values, you can edit the IF
IF TAG_C1 <> TAG_C2
into
IF TAG_C1 <> TAG_C2 or TAG_C1 is null or TAG_C2 is null
or, more compact, but less readable:
IF nvl(TAG_C1, 'a value that a clan can never have') <> nvl(TAG_C2 , 'some other impossible value')

Insert data from a datatable and declared variable into a table using a stored procedure

I am trying to insert asp.net grid data to database table [DBO].[tbl_family_member]. I've created a type typ_fam_mem for this. But the problem is the column family_member_id value is generated from another table [DBO].[TBL_FAMILY_HEAD]. Please look at the procedure.
How can I insert data to the table from a datatable and procedure variables?
CREATE TABLE [DBO].[tbl_family_member]
(
family_member_id bigint identity(1,1) not null,
family_head_id bigint NOT NULL,
mem_name nvarchar(200) not null,
mem_gender tinyint not null,
mem_occupation nvarchar(50),
mem_maritial_status tinyint,
STATUS INT NOT NULL DEFAULT '1',
CONSTRAINT pk_family_member PRIMARY KEY (family_member_id)
)
create type typ_fam_mem as table
(
mem_name nvarchar(200) not null,
mem_gender tinyint not null,
mem_occupation nvarchar(50),
mem_maritial_status tinyint
)
Stored procedure
CREATE PROCEDURE [dbo].[P_SET_PROFILE_REGISTRATION]
(
--- FOR FAMILY HEAD TABLE
#P_NAME NVARCHAR(200),
#P_GENDER TINYINT,
---- FOR FAMILY MEMBER
#P_FAMILY_DT DBO.typ_fam_mem READONLY,
#V_OUT TINYINT OUTPUT
)
AS
DECLARE #FAMILY_HEAD_ID BIGINT;
BEGIN
SET NOCOUNT ON;
BEGIN TRY
INSERT INTO [DBO].[TBL_FAMILY_HEAD] ([NAME], [GENDER])
VALUES (#P_NAME, #P_GENDER)
SET #FAMILY_HEAD_ID = SCOPE_IDENTITY();
IF ##ROWCOUNT > 0
BEGIN
--- block for inserting data into [DBO].[tbl_family_member]
END
END TRY
BEGIN CATCH
END CATCH
END
#P_FAMILY_DT is a table type, you can query the table type like a normal table.
Your insert should look like
INSERT INTO [DBO].[tbl_family_member]
(family_head_id,
mem_name,
mem_gender,
mem_occupationm,
mem_maritial_status)
SELECT #FAMILY_HEAD_ID,
mem_name,
mem_gender,
mem_occupation,
mem_maritial_status
FROM #P_FAMILY_DT
You can use INSERT...SELECT, specifying the variable with the generated value. The example below also adds a transaction management (appropriate for multi-table inserts unless client app manages the transaction) and error handling.
CREATE PROCEDURE [dbo].[P_SET_PROFILE_REGISTRATION]
--- FOR FAMILY HEAD TABLE
#P_NAME NVARCHAR(200),
#P_GENDER TINYINT,
---- FOR FAMILY MEMBER
#P_FAMILY_DT DBO.typ_fam_mem READONLY,
#V_OUT TINYINT OUTPUT
AS
DECLARE #FAMILY_HEAD_ID BIGINT;
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN;
INSERT INTO [DBO].[TBL_FAMILY_HEAD] ([NAME], [GENDER])
VALUES (#P_NAME, #P_GENDER)
SET #FAMILY_HEAD_ID = SCOPE_IDENTITY();
INSERT INTO dbo.tbl_family_member(
mem_name
, mem_gender
, mem_occupation
, mem_maritial_status
, family_head_id
)
SELECT
mem_name
, mem_gender
, mem_occupation
, mem_maritial_status
, #FAMILY_HEAD_ID
FROM #P_FAMILY_DT;
COMMIT;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
GO

SQL Error: No more data to read from socket while inserting data in table

I have created a table in oracle xe
create table tbl_unit_mst
(
id number(10,0) constraint id_pk primary key,
unit_code char(2) not null constraint unit_code_uk unique,
unit_name varchar2(30) not null constraint unit_name_uk unique,
crtd_date date default sysdate,
is_active number(1,0) default 1 constraint is_active_ck check(is_active in (0,1)),
crtd_by varchar2(6)
);
and then created a squence
create sequence seq_tbl_unit
start with 1
increment by 1
nocache
nocycle;
then I created a Trigger
create trigger trig_id_increment
before insert
on tbl_unit_mst for each row
begin
select seq_tbl_unit.nextval into : new.id from dual;
end;
Now when I am trying to run an insert statement
insert into tbl_unit_mst ( unit_code, unit_name) values('01','Ajbapur');
it gives an error SQL Error: No more data to read from socket
If I disable Trigger then it is working fine.
can anyone help me to find out where I am making mistakes

Auto Increment varchar datatype set as primary key

I have created a table with an Id column as varchar(20).
I need a stored procedure which can increment id by 1.
I have tried this:
ALTER PROCEDURE dbo.spInsertCatQuery
(#Users_Id varchar(20),
#Cat_Id varchar(20),
#Query varchar(100),
#Query_Title varchar(50)
)
AS
BEGIN
Declare #Query_Id bigint
SELECT #Query_Id = coalesce((select max(Query_Id) + 1 from tblCatQuery), 1);
INSERT INTO tblCatQuery
VALUES(#Query_Id, #Users_Id, #Cat_Id, #Query_Title, #Query)
END
But it is not working after 10th record.
Change the selection of Query_id from your table to below
SELECT #Query_Id=
coalesce((select max(cast(Query_Id as int)) + 1 from tblCatQuery), 1);
Based on Gordon's comment; my understanding is that since ID is varchar max(id) is not fetching the correct max value but casting it will do so.
For example try this
create table testtab (id varchar(10));
insert into testtab values(2),(200),(53)
If you say below it will return 53
select MAX(id) from testtab
but this one will return 200
select MAX(cast(id as int)) from testtab
Tested in SQL SERVER 2008 R2
You do know your stored procedure has an implicit race condition, don't you?
Between your calculating the new query id and your table insert getting committed, another session can come in, get exactly the same query id, insert it and get committed. Guess what happens when your insert tries to commit? First in wins; the second gets a duplicate key error. Don't ask me how I know this :)
If you really need a text query id, you might try using a computed field, something like this:
create table dbo.tblCatQuery
(
query_id int not null identity(1,1) primary key clustered ,
query_id_text as right('0000000000'+convert(varchar,id),10) ,
user_id varchar(20) not null ,
cat_id varchar(20) not null ,
query varchar(100) not null ,
query_title varchar(50) not null ,
)
Then your stored procedure looks like this:
create procedure dbo.spInsertCatQuery
#Users_Id varchar(20) ,
#Cat_Id varchar(20) ,
#Query varchar(100) ,
#Query_Title varchar(50) ,
#Query_ID varchar(10) output
AS
insert dbo.tblCatQuery ( user_id , cat_id , query_title , query )
VALUES ( #Users_Id , #Cat_Id , #Query_Title , #Query )
-- give the caller back the id of the row just inserted
set #Query_ID = ##SCOPE_IDENTITY
-- for redundancy, hand it back as the SP's return code, too
return #Query_ID
GO
It sounds like your application needs a string for the ID field, yet in the database you want it ID to behave as an auto-incrementing integer field.
Consider using an integer in the database, and when you retrieve the value and need to use it as as string, at that point convert the value to a string, either in your query or in your application.
This will solve your problem.
You must seriously review your design. I shall suggest something like this.
CREATE TABLE tblCatQuery(QueryId int NOT NULL PRIMARY KEY IDENTITY(1, 1),
UserId int NOT NULL REFERENCES tblUsers(UserId),
CatId int NOT NULL REFERENCES tblCat(CatId),
Query varchar(100), Query_Title varchar(50))
CREATE TABLE tblUsers(UserId int NOT NULL PRIMARY KEY IDENTITY(1, 1), ....
CREATE TABLE tblCat(CatId int NOT NULL PRIMARY KEY IDENTITY(1, 1), ....
CREATEPROCEDURE dbo.spInsertCatQuery
(
#Users_Id int,
#Cat_Id int,
#Query varchar(100),
#Query_Title varchar(50)
)
AS
BEGIN
INSERT INTO tblCatQuery(Users_Id, Cat_Id, Query_Title, Query)
VALUES( Users_Id, Cat_Id, Query_Title, Query)
END

Resources