inserting values from another table via a TRIGGER in MYSQL - mysql

I need to create a trigger when a new record is added to the "plan" table, a record is automatically created in the "results" table.
Table "Plan" has columns:
IdService
IdEmployee
Groupe
Date
Type (varchar)
Table "Results" has columns:
IdService
IdEmployee
IdClient
Date
Result (varchar)
But the idClient must be taken from table "Clients", corresponding to the group number added to the plan. Thus, the trigger should create not one, but several rows in the result table (since there can be several clients in one group)
I am attaching my code, but there is obviously an error in it
CREATE DEFINER = CURRENT_USER TRIGGER `mydb`.`Plan_AFTER_INSERT` AFTER INSERT ON `Plan` FOR EACH ROW
BEGIN
insert into results (Result, idClient, Date, idService, idEmployee)
values ('в процессе',idClient = (Select idClient from Clients
where Clients.Groupe = Plan.New.Groupe),
NEW.Date, NEW.idService, NEW.idEmployee);
END

It is quite OK only your subquery has ti return only one row, so it is better to limit it
DELIMITER //
CREATE DEFINER = CURRENT_USER TRIGGER `mydb`.`Plan_AFTER_INSERT` AFTER INSERT ON `Plan` FOR EACH ROW
BEGIN
insert into results (Result, idClient, Date, idService, idEmployee)
values ('в процессе',(Select idClient from Clients
where Clients.Groupe = New.Groupe ORDER BY idClient LIMIT 1),
NEW.Date, NEW.idService, NEW.idEmployee);
END//
DELIMITER ;

Related

MySQL - Can't update table in stored function/trigger because it is already used by statement which invoked this stored function/trigger (select)

I am new to MySQL and learning about trigger. I have 2 tables that I want : when a table (detail_transaction) has been inserted, a 'stock' field of another table (item) change.
'item' Table
id
name
price
stock
1
Item_A
15
900
2
Item_B
9
500
'detail_transaction' Table
id
id_item
count
total_price
1
1
5
75
If I insert new row in 'detail_transaction' table, I WANT my 'stock' field in 'item' table with the same 'id' to decrease and adjust to the 'count' of the 'detail_transaction'. For example :
I insert new row in 'detail_transaction' table :
id
id_item
count
total_price
2
1
10
150
I WANT the 'item' table updated to :
id
name
price
stock
1
Item_A
15
890
2
Item_B
9
500
I created a trigger to try achieve my purpose, but when I tried to insert new row in 'detail_transaction' I got this error : Can't update 'item' table in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
My trigger :
DELIMITER $$
CREATE TRIGGER update_stock
AFTER INSERT
ON detail_transaction
FOR EACH ROW
BEGIN
UPDATE item
JOIN detail_transaction ON detail_transaction.id_item = item.id
SET stock = stock - NEW.count
WHERE item.id = NEW.id_item;
END$$
DELIMITER ;
Then, I inserted row to detail_transaction table :
INSERT INTO detail_transaction (id, id_item, count, total_price)
VALUES (2, 1, 10, (SELECT price FROM item WHERE item.ID = 1) * 10);
But I got the error. What can I do to solve this? Is it because of the SELECT part when I try to INSERT? Thanks for your answer.
Firstly (and opinionated): triggers are hard to debug, test and maintain. Systems that include triggers are really hard to debug because they introduce side effects - "I did X on this table, and then Y happened on a different table". As a developer, you have to keep all the triggers in your head to understand what an individual statement might do.
If we take your example, for instance, you might have a trigger on the "stock" field in Item to create a purchase order record to replenish the stock if it falls below a threshold. The purchase order table might have an insert trigger to create a record in accounts payable, which might have an insert trigger to reject records if the total balance for a given vendor exceeds a threshold. That chain of triggers implements valid business logic, but results in really complex debugging process when suddenly an insert into detail_transaction is rejected because the product vendor exceeds their payment limit. (And yes, I have seen this kind of scenario!).
One of the challenges with triggers is that the database engine does not want an infinite loop to happen, or to have the value of the field you are SELECTing changing as a result of the trigger firing.
Also, you don't need that join - you can get the values from NEW.
DELIMITER $$
CREATE TRIGGER update_stock
AFTER INSERT
ON detail_transaction
FOR EACH ROW
BEGIN
UPDATE item
SET stock = stock - NEW.count
WHERE item.id = NEW.id_item;
END$$
DELIMITER ;
The way to do this is to use a variable:
SET #PRICE = ((SELECT price FROM item WHERE item.ID = 1) * 10);
INSERT INTO detail_transaction (id, id_item, count, total_price)
VALUES (2, 1, 10, #PRICE);
SELECT * from item;
See fiddle.
EDIT - some of the other answers show a simpler solution - calculating the total price in a trigger.
Reasonable people disagree about how to use triggers - but I would suggest that using triggers to calculate derived values - "total stock for a given item", or "total price of a transaction" - is often a bad idea. You're effectively duplicating data - the total stock level for an item is both the sum of transactions, and the attribute in a row. The total price is both "price * quantity", and an attribute in a row. What happens if someone executes an update statement for total_price or total_stock (either intentionally or as part of a bug)? Which value is correct?
You should not mix insert..values and insert..select I would rewrite the insert as
INSERT INTO detail_transaction (id, id_item, count, total_price)
select 2, 1, 10, price * 10
FROM item
WHERE item.ID = 1;
Although my choice would be a before insert trigger
DELIMITER $$
CREATE TRIGGER update_stock before INSERT ON detail_transaction
FOR EACH ROW
BEGIN
set new.total_price = (
select item.price * new.count
FROM item
WHERE item.ID = new.id
);
END$$
DELIMITER ;
with an insert
INSERT INTO detail_transaction (id, id_item, count, total_price)
VALUES (2, 1, 10, null);
The after insert publish by you fails because you use a multi table update invoking a table which fired the trigger, this is not allowed , the resolution of this issue appear in a previous answer.
CREATE TABLE item (
`id` INTEGER AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255),
`price` INTEGER,
`stock` INTEGER
);
INSERT INTO item VALUES
('1', 'Item_A', '15', '900'),
('2', 'Item_B', '9', '500');
SELECT * FROM item;
CREATE TABLE detail_transaction (
`id` INTEGER AUTO_INCREMENT PRIMARY KEY,
`id_item` INTEGER,
`count` INTEGER,
`total_price` INTEGER,
FOREIGN KEY (`id_item`) REFERENCES `item` (`id`)
);
INSERT INTO detail_transaction VALUES
('1', '1', '5', '75');
SELECT * FROM detail_transaction;
id
name
price
stock
1
Item_A
15
900
2
Item_B
9
500
id
id_item
count
total_price
1
1
5
75
-- trigger which calculates total_price value
CREATE TRIGGER tr_bi_get_total_price
BEFORE INSERT ON detail_transaction
FOR EACH ROW
SET NEW.total_price = (
SELECT NEW.`count` * item.price
FROM item
WHERE id = NEW.id_item
);
-- trigger which adjusts stock value
CREATE TRIGGER tr_ai_update_stock_in_item
AFTER INSERT ON detail_transaction
FOR EACH ROW
UPDATE item
SET stock = stock - NEW.count
WHERE item.id = NEW.id_item;
INSERT INTO detail_transaction (id_item, `count`) VALUES (1, 10);
SELECT * FROM detail_transaction;
SELECT * FROM item;
id
id_item
count
total_price
1
1
5
75
2
1
10
150
id
name
price
stock
1
Item_A
15
890
2
Item_B
9
500
fiddle
PS. Each trigger contains only one statement. So neither BEGIN-END noк DELIMITER command needed.

Update mysql rows with formula having different values for each row

I have two tables:
Table 1 : parts
id (primary key)
code
title
quantity
Table 2 : bill_items
id (primary key)
bill_id
parts_id: refers to primary key of table parts
qty
I would like to update parts table - qty every time I create a row in table bill_items. The qty in parts table is to be decremented by qty of bill_items. There may be N number of updates to table bill_items in one go. Would like to use a single INSERT....ON DUPLICATE or UPDATE statement.
Thank you for your time.
I think for this case better using trigger :
DELIMITER $$
CREATE
TRIGGER `bill_items_after_insert` AFTER INSERT
ON `bill_items`
FOR EACH ROW BEGIN
UPDATE parts set quantity = quantity - NEW.qty WHERE id = NEW.parts_id;
END$$
DELIMITER ;
I suggest you also make trigger for UPDATE and DELETE also for data consistency.
UPDATED
Based on your comment, it is possible to use normal insert and update using transaction for consistency data :
START TRANSACTION;
INSERT INTO bill_items (bill_id, parts_id, qty) VALUES (your_bill_id,your_parts_id,your_qty);
UPDATE parts SET quantity = quantity - your_qty WHERE id = your_parts_id;
COMMIT;

Mysql column count doesn't Match value count (but it does!)

I am running the following procedure in mysql:
create procedure addSavingAccount(id int(10), account_number varchar(10))
begin
insert into saving values(id,'savings', account_number, 0);
end //
However, when I try to call it, it gives me this error:
mysql> call addSavingAccount(103, 'B505');
ERROR 1136 (21S01): Column count doesn't match value count at row 1
I checked on anything that could be linked to it, including triggers. But everything seems like it should be working. Here is my list of triggers:
create trigger balance_change_saving after update on saving for each row
begin
if old.balance != new.balance
then
insert into balance_update_history values(null, 'saving', new.account_number, old.balance, new.balance,
(SELECT NOW()), (select USER()));
end if;
end//
create trigger balance_insert_saving after insert on saving for each row
begin
insert into balance_update_history values(null, 'saving', new.account_number, 0, new.balance, (select now()),
(select user()));
end //
create trigger balance_delete_saving after delete on saving for each row
begin
insert into balance_update_history values(null, null, null, old.balance, null,
(SELECT NOW()), (select USER()));
end //
And here is where I define the table:
create table if not exists saving(account_number varchar(10) , customer_id int(10), balance decimal(8,2), primary key(account_number));
I'd just really like to to figure this out.
There are three columns based on your table create statement, not four. (what is the last 0 in that insert?)
Also, in the procedure, it appears that your insert values are out-of-order relative to the table creation order? So you can either rearrange the insert values to match the table, OR specify the columns with the insert.

Mysql after insert trigger inserting 1st row twice

I am using mysql work bench. I have two tables "new_machine"(parent table) and "machine_record" (child table). I am using after insert trigger to insert m_id and m_name from new_machine table to mac_id and mac_name from machine_record table.
m_id is primary key for new_machine.
m_id and m_name from new_machine table is foreign key to mac_id and mac_name from machine_record table.
when i insert data in new_machine it populates in machine_record but the first record entered is entered twice.
CREATE DEFINER=`root`#`localhost` TRIGGER `system_data`.`new_machine_AFTER_INSERT` AFTER INSERT ON `new_machine` FOR EACH ROW
BEGIN
insert into `system_data`.`machine_record` (mac_id, mac_name)
select m_id, m_name
from `system_data`.`new_machine`;
END
you can direct use the Values from your insert on new_machines. So
it is not necessary to get it with a SELECT. Also your
SELECT selects ALL Records from new_machines.
CREATE DEFINER=`root`#`localhost` TRIGGER `system_data`.`new_machine_AFTER_INSERT` AFTER INSERT ON `new_machine` FOR EACH ROW
BEGIN
insert into `system_data`.`machine_record` (mac_id, mac_name)
VALUES (NEW.m_id, NEW.m_name);
END

Get current updated column name to use in a trigger

Is there a way to actually get the column name that was updated in order to use it in a trigger?
Basically I'm trying to have an audit trail whenever a user inserts or updates a table (in this case it has to do with a Contact table)
CREATE TRIGGER `after_update_contact`
AFTER UPDATE ON `contact` FOR EACH ROW
BEGIN
INSERT INTO user_audit (id_user, even_date, table_name, record_id, field_name, old_value, new_value)
VALUES (NEW.updatedby, NEW.lastUpdate, 'contact', NEW.id_contact, [...])
END
How can I get the name of the column that's been updated and from that get the OLD and NEW values of that column. If multiple columns have been updated in a row or even multiple rows would it be possible to have an audit for each update?
Just use OLD.colname <> NEW.colname for each column to check and find those that are different. Triggers are a bit limited in their use in MySQL, too bad.
Try this code...
create table sales (
orderno INT,
sale INT,
empsalary int,
ts TIMESTAMP
);
create table history (
updated varchar(20),
oldvalue INT,
newvalue INT
);
INSERT INTO sales
(orderno, sale, empsalary)
VALUES
(1,700,7000),
(2,800,8000),
(3,900,9000);
DROP TRIGGER test.instrigger;
DELIMITER ///
CREATE TRIGGER test.instrigger
AFTER UPDATE ON sales
FOR EACH ROW
BEGIN
IF NEW.sale <> OLD.sale THEN
INSERT INTO history
(updated, oldvalue, newvalue)
VALUES
('sale', OLD.sale,NEW.sale);
END IF;
IF NEW.empsalary <> OLD.empsalary THEN
INSERT INTO history
(updated, oldvalue, newvalue)
VALUES
('empsalary', OLD.empsalary,NEW.empsalary);
END IF;
END;
///
DELIMITER ;