I am just want to create after insert trigger to insert a new row in history table. Why am I getting an error when I run the query?
orders
create table orders
(
id int auto_increment
primary key,
id_user int not null,
picture_name varchar(100) not null,
time date not null,
constraint FK_USER
foreign key (id_user) references stef.users (id)
)
;
create index FK_USER_idx
on orders (id_user)
;
history
create table history
(
id int auto_increment
primary key,
id_order int not null,
id_action int not null,
time date not null,
constraint FK_ORDER
foreign key (id_order) references stef.orders (id),
constraint FK_ACTION
foreign key (id_action) references stef.actions (id)
)
;
create index FK_ORDER_idx
on history (id_order)
;
create index FK_ACTION_idx
on history (id_action)
;
my trigger...
CREATE TRIGGER orders_AFTER_INSERT
AFTER INSERT ON stef.orders
FOR EACH ROW
BEGIN
INSERT INTO history('id_order', 'id_action', 'time')
VALUES (NEW.id, 1, NOW());
END;
I am just want to create after insert trigger to insert a new row in history table. Why am I getting an error when I run the query?
Try this
DELIMITER $$
CREATE TRIGGER orders_AFTER_INSERT
AFTER INSERT ON stef.orders
FOR EACH ROW
BEGIN
INSERT INTO history(`id_order`, `id_action`, `time`)
VALUES (NEW.id, 1, NOW());
END$$
DELIMITER ;
You need to temporarily override the delimiter so MySQL can differentiate between the end of a statement within the body of a trigger (or procedure, or function) and the end of the body.
Edit: Single quotes (') are only ever used to denote string values, for field names use the ` (or in some configurations the ")
CREATE TRIGGER orders_AFTER_INSERT
AFTER INSERT ON stef.orders
FOR EACH ROW
BEGIN
INSERT INTO stef.history()
VALUES (null, NEW.id, 1, NOW());
END
Related
i am trying to get used to triggers. I created a small database and a trigger. When i go to insert something in ship category it does not let me do it.If i drop the trigger with the same commend i can insert values at the table. I get this error: #1048 - Column 'IMO' cannot be null
My trigger code is:
/*ship insert*/
DELIMITER //
CREATE TRIGGER `ship_insert_logs`
AFTER INSERT ON `ship`
FOR EACH ROW
BEGIN
DECLARE ship_IMO INTEGER;
SET ship_IMO=new.IMO;
INSERT INTO ship_logs VALUES (null, concat('A new row is inserted with IMO ', ship_IMO, 'at',
date_format(now(), '%d-%m-%y %h:%i:%s %p')));
END //
DELIMITER ;
and ship table is:
CREATE TABLE ship(
department_id INTEGER NOT NULL,
IMO BIGINT PRIMARY KEY NOT NULL,
Latitude DOUBLE PRECISION NOT NULL,
Longitude DOUBLE PRECISION NOT NULL,
current_speed DOUBLE PRECISION NOT NULL,
heading VARCHAR (30),
status VARCHAR(30),
FOREIGN KEY(department_id) REFERENCES department (department_id) ON UPDATE CASCADE
);
while ship_logs table is:
CREATE TABLE ship_logs(
IMO BIGINT PRIMARY KEY NOT NULL,
audit_description VARCHAR(500)
);
I have defined the following trigger on my database in phpMyAdmin:
It's a very simple trigger where I just insert a record into the history table after an insert in the item table:
insert into history values(null, new.Id, new.LocationId, new.LocationId, new.PersonId, new.PersonId, date(now()))
However, the trigger is not firing (and it worked correctly when I tested it in MySQL Workbench).
Edit 1:
The trigger fires when I define it with some dummy values, like this:
insert into history values(null, 1, 1, 1, 1, 1, date(now()))
Edit 2:
The history table is written into when I define the trigger with dummy values (see edit 1). But the most recently added entry's ID is 11 (ID is auto_increment), even though the last entry before that had ID = 3. My point is that they are not consecutive, but rather as if there were entries in between but were deleted for some reason...
Edit 3:
Here is the DDL for item and history tables:
create table Item
(
Id int not null auto_increment primary key,
InventoryNumber int not null unique,
ItemStatus varchar(20) not null,
ItemType varchar(30) not null,
ItemName varchar(100) not null,
PurchaseDate date not null,
PurchaseValue decimal(12,4) not null,
Amortization decimal(12,4) not null,
LocationId int not null,
PersonId int not null,
foreign key(PersonId) references Person(Id),
foreign key(LocationId) references Location(Id)
);
create table History
(
Id int not null auto_increment primary key,
ItemId int not null,
OldLocationId int not null,
NewLocationId int not null,
OldPersonId int not null,
NewPersonId int not null,
DateOfChange date not null,
foreign key(ItemId) references Item(Id),
foreign key(OldLocationId) references Location(Id),
foreign key(NewLocationId) references Location(Id),
foreign key(OldPersonId) references Person(Id),
foreign key(NewPersonId) references Person(Id)
);
and the trigger (which I think is irrelevant since I didn't import the trigger via a script, I defined it using phpMyAdmin's GUI):
DELIMITER //
CREATE TRIGGER historyTriggerInsert AFTER INSERT ON Item
FOR EACH ROW
BEGIN
insert into History values(null, new.Id, new.LocationId, new.LocationId, new.PersonId, new.PersonId, date(now()));
END;//
DELIMITER ;
And here is another thing I found out: when I add an entry to item using my Yii web application, the entry is visible in the item table in phpMA, but the trigger inserts nothing into the history table. However, when I add an entry to item directly in phpMA, the trigger also creates an entry in history i.e. it works correctly.
Also, when I define the trigger with those dummy values but only leave new.Id, like this:
insert into history values(null, new.Id, 1, 1, 1, 1, date(now()))
it also doesn't work. This and the incrementing ID leads me to a conclusion that the trigger is fired, tries to insert into history, but for some reason gets an invalid new.Id value.
I'm trying to create trigger on customer-order database where each customer has several orders and each order has several items.
I'm planning to create a trigger to ensure that
the total number of all orders place by the same customer cannot
exceed 10000
How can create the insert trigger for above constraint.
Here is my SQL file with sample data provided.
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `mydb` ;
CREATE TABLE customers
(`id` int not null auto_increment primary key, `first_name` varchar(64), `last_name`varchar(64) );
INSERT INTO customers(`first_name`, `last_name`)VALUES('Jhon', 'Doe');
CREATE TABLE items
(`id` int not null auto_increment primary key,`item` varchar(64),`price` decimal(19,2));
INSERT INTO items(`item`, `price`)VALUES('Item1', 10.5),('Item2', 25);
CREATE TABLE orders
(`id` int not null auto_increment primary key, `date` date, `customer_id` int,`status` int not null default 1, -- 1 new constraint fk_customer_id foreign key (customer_id) references customers (id));
INSERT INTO orders(`date`, `customer_id`, `status`)VALUES(CURDATE(), 1, 1);
CREATE TABLE order_items(`id` int not null auto_increment primary key,
`order_id` int not null, `item_id` int not null, `quantity` decimal(19,3) not null, `price` decimal(19,3) not null,
constraint fk_order_id foreign key (order_id) references orders (id),
constraint fk_item_id foreign key (item_id) references items (id));
INSERT INTO order_items(`order_id`, `item_id`, `quantity`, `price`)VALUES
(1, 1, 2, 10.5),(1, 2, 4, 25);
;
Although Jahul's answer would technically work, here is alternative logic:
DELIMITER $$
CREATE TRIGGER `customer_orders_check`
BEFORE INSERT ON `orders` FOR EACH ROW
BEGIN
IF ((select count(*)
from `orders`
where a.customer_id = NEW.customer_id
) >= 10000 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Too many orders already';
END IF;
END;
$$
DELIMITER ;
That said, I would suggest an alternative approach. Counting up to 10,000 rows for each insert seems like a lot of work. Instead, keep the counter in the customers table, using an after insert trigger (and perhaps after update/delete as well). Then when inserting a new row, you can just check the count in customers.
This trigger will stop insert --
CREATE TRIGGER `customer_orders_check`
BEFORE INSERT ON `orders` FOR EACH ROW
BEGIN
IF exists(select count(*)
from `orders` a
where a.customer_id= NEW.customer_id
having count(*)>=10000 ) THEN
SET NEW.id = 1 ;
END IF;
END;
I have multiple user_roles. Each user_role has multiple privileges and each privileges has multiple values. I need to create a procedure with user_role_name,description,priviliges_fk(array),values(arrayofstring) as inputs.
This is the procedure I have written.
DELIMITER $$
DROP PROCEDURE IF EXISTS `save_role`$$
CREATE DEFINER=`event_admin`#`%` PROCEDURE `save_role`(IN p_role_name INT,
IN p_description INT,
IN p_privilege_fk INT(),
IN p_values VARCHAR(1000)
)
BEGIN
DECLARE i int default 0;
DECLARE V_ROLE_FK int;
DECLARE counter INT DEFAULT 0;
INSERT INTO ROLE (ROLE_NAME,DESCRIPTION) VALUES(p_role_name,p_description);
SELECT ROLE_PK INTO V_ROLE_FK FROM ROLE WHERE ROLE_NAME=p_role_name AND DESCRIPTION=p_description;
simple_loop:LOOP
SET counter = counter + 1;
INSERT INTO ROLE_PRIVILEGE_BRIDGE (ROLE_FK,PRIVILEGE_FK,VALUE) VALUES(V_ROLE_FK,p_privilege_fk(i),p_values);
END LOOP simple_loop;
END;
You can't. There are two workarounds that would work
Call the procedure one time per element in the array
Concatenate the array elements into one string separated by something (ie |, ;, :) and then split that string internally in the procedure.
I would go with the first alternative. It's cleaner, easier to understand and easier to test.
I'd suggest you to use AUTO_INCREMENT option for primary keys, it will help to work with them. Then use auto-incremented primary key values to insert new rows into a child table - one by one, not using array as a string parameter.
For example (data is simplified):
CREATE TABLE ROLE(
ID INT(11) NOT NULL AUTO_INCREMENT,
ROLE_NAME INT,
DESCRIPTION INT,
PRIMARY KEY (ID)
)
ENGINE = INNODB;
CREATE TABLE ROLE_PRIVILEGE_BRIDGE(
ID INT(11) NOT NULL AUTO_INCREMENT,
PRIVILEGE_FK INT(11) DEFAULT NULL,
VALUE INT(11) DEFAULT NULL,
PRIMARY KEY (ID),
CONSTRAINT FK FOREIGN KEY (PRIVILEGE_FK) REFERENCES ROLE (ID)
)
ENGINE = INNODB;
INSERT INTO ROLE(ROLE_NAME, DESCRIPTION) VALUES(1, 1);
SET #new_id = LAST_INSERT_ID();
INSERT INTO ROLE_PRIVILEGE_BRIDGE(PRIVILEGE_FK, VALUE) VALUES (#new_id, 1);
INSERT INTO ROLE_PRIVILEGE_BRIDGE(PRIVILEGE_FK, VALUE) VALUES (#new_id, 2);
INSERT INTO ROLE_PRIVILEGE_BRIDGE(PRIVILEGE_FK, VALUE) VALUES (#new_id, 3);
I have such example:
CREATE TABLE a(
id INT PRIMARY KEY AUTO_INCREMENT,
parent_id INT,
FOREIGN KEY (parent_id) REFERENCES a(id)
);
DELIMITER ;;
CREATE TRIGGER a_insert BEFORE INSERT ON a
FOR EACH ROW
BEGIN
SIGNAL SQLSTATE '01431' SET MESSAGE_TEXT = 'The foreign data source you are trying to reference does not exist.';
END;;
DELIMITER ;
INSERT INTO a(parent_id) VALUES (NULL);
INSERT INTO a(parent_id) VALUES (1);
INSERT INTO a(parent_id) VALUES (2);
INSERT INTO a(parent_id) VALUES (4);
INSERT INTO a(parent_id) VALUES (999);
SELECT * FROM a
This end up with 4 recods:
----------------
id parent_id
----------------
1 NULL
2 1
3 2
4 4
I found post online that MySQL does not support rollbacks in triggers. That is a problem because I want such hierarchy where no row points to it self and inserts like the one with ID=4 works just fine. How do I ensure there are no such records in database?
Well, the problem is with auto_increment, because you in BEFORE INSERT event you don't have that value assigned yet. On the other hand in AFTER INSERT event you can't do anything with it.
If you want to use auto_increment id column a possible solution is to use separate table for sequencing.
Your schema would look like
CREATE TABLE a_seq(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE a(
id INT NOT NULL PRIMARY KEY DEFAULT 0,
parent_id INT,
FOREIGN KEY (parent_id) REFERENCES a(id)
);
And your trigger
DELIMITER $$
CREATE TRIGGER a_insert
BEFORE INSERT ON a
FOR EACH ROW
BEGIN
INSERT INTO a_seq VALUES(NULL);
SET NEW.id = LAST_INSERT_ID();
IF NEW.id = NEW.parent_id THEN
SET NEW.id = NULL;
END IF;
END$$
DELIMITER ;
If id=parent_id the trigger deliberately violates NOT NULL constraint assigning NULL value. Therefore this record won't be inserted.
Here is SQLFiddle demo. Uncomment last insert statement. It won't allow you to make such insert.