I've got this mysql table:
CREATE TABLE IF NOT EXISTS `activities` (
`id` INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` CHAR(255) NOT NULL,
`status` ENUM('open','progress','close'),
`date_begin` DATE,
`date_finish` DATE,
`progress` TINYINT,
`reliance` INTEGER,
`parent` INTEGER,
FOREIGN KEY(owner) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL,
FOREIGN KEY(reliance) REFERENCES activities(id) ON UPDATE CASCADE ON DELETE SET NULL,
FOREIGN KEY(parent) REFERENCES activities(id) ON UPDATE CASCADE ON DELETE SET NULL
)ENGINE = INNODB;
My problem is when i want to update the date_begin of one activity. Infact i would like to update the date of begin of all activities that are reliant or child of the updated activity.
Can i force mysql to create a recursive trigger?
Something like this should work, at least if you're on a recent version of MySQL:
delimiter |
CREATE TRIGGER activitiesUpdateReliantAndChildren BEFORE UPDATE ON 'activities'
FOR EACH ROW
BEGIN
update 'activities'
set date_begin = NEW.date_begin
where reliance = NEW.id;
update 'activities'
set date_begin = NEW.date_begin
where parent = NEW.id;
END;
|
delimiter ;
Although the trigger may work...it is not a good practice to create recursive triggers! and if u thing you must... then think not only twice but 100times before applying! They can result in more harm than good sometimes.
Related
I try to use View to create a constraint on a Mysql Table.
I have one table with cinemas sessions and one other for tickets, the goal is we can't sell tickets when the session is full.
The session has a number of places, for example, 300 places. When one ticket is sold, the number of places decreases. The rest is 299. (this is what I do with a view)
I will check before the data is written if the amount of places is enough.
The condition is place_left >= number (of tickets wanted).
For example, the rest of places is 5 and a user wants 6 places, the request is rejected because the condition is not true.
The condition is place_left >= number (of tickets wanted).
Actually I use two views to do that (not tested, just created), I think I can do this in one shot but I failed.
I have seen this possibility here:
https://dev.mysql.com/doc/refman/8.0/en/view-check-option.html
I first tried something like that:
CREATE VIEW view_place_left AS
SELECT sessions.id,
(SELECT places FROM rooms WHERE id=sessions.room_id)-(SELECT SUM(number) AS count FROM tickets WHERE session_id=sessions.id)
AS place_left FROM sessions WHERE place_left>=0
WITH CHECK OPTION;
, but the part WHERE place_left>=0 is not working.
So I do the job in two steps
The first view:
CREATE VIEW view_place_left
AS SELECT sessions.id,
(SELECT places FROM rooms WHERE id=sessions.room_id)-(SELECT SUM(number) AS count FROM tickets WHERE session_id=sessions.id)
AS place_left FROM sessions;
And the second view:
CREATE VIEW check_view_place_left AS
SELECT id, place_left FROM view_place_left WHERE place_left>=0
WITH CHECK OPTION;
That seems ok, but I think it's possible to do the job in a single view, someone can help?
I work with MySql 8.0
First Add :
create sessions table :
CREATE TABLE IF NOT EXISTS sessions
(
id VARCHAR(36) NOT NULL PRIMARY KEY DEFAULT (UUID()),
movie_id VARCHAR(36),
room_id VARCHAR(36),
date DATETIME,
FOREIGN KEY (movie_id) REFERENCES movies(id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
) ENGINE = InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
Create tickets table :
CREATE TABLE IF NOT EXISTS tickets
(
id VARCHAR(36) NOT NULL PRIMARY KEY DEFAULT (UUID()),
session_id VARCHAR(36) NOT NULL,
user_id VARCHAR(36) NOT NULL,
price_id VARCHAR(36) NOT NULL,
number INT NOT NULL,
date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
paid INT,
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (price_id) REFERENCES prices(id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
SOLUTION
So i found my solution.
To Check and validate the place left, i just need a trigger before insert.
Here is my code :
\! echo "\033[33m*** Create trigger on 'tickets' ***\033[m";
DELIMITER $$
CREATE TRIGGER check_places BEFORE INSERT ON tickets
FOR EACH ROW
BEGIN
IF PlaceLeft(NEW.session_id, NEW.number) != 1 THEN SIGNAL sqlstate '45000' set message_text = "No enough places";
END IF;
END$$
DELIMITER ;
The function PlaceLeft
DELIMITER $$
CREATE FUNCTION PlaceLeft(session_id VARCHAR(36), number INT)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE places INT;
SELECT place_left INTO places FROM view_place_left;
IF places>=number THEN RETURN 1;
ELSE RETURN 0;
END IF;
END$$
Maybe that can help another lost soul :)
This stored procedure should insert an entry after a SELECT for a foreign key. Currently, It does nothing. When I CALL it, it won't give me an error or anything and won't insert the record either.
Can I even do multiple things (SELECT, UPDATE, INSERT..) in a single procedure? If yes, what am I missing here? If there is an error while running one of the (update/insert/select)s, should I get an error?
DELIMITER $$
CREATE PROCEDURE `InserAndAttachReferencia` (
IN refereciaText VARCHAR(45),
IN attachTo INT unsigned,
OUT insid INT unsigned
)
BEGIN
-- SELECT THE REP_ID
DECLARE attachToRepID INT UNSIGNED DEFAULT 0;
SELECT id
INTO attachToRepID
FROM AlkReferencia
WHERE id=attachTo;
-- INSERTING THE NEW ENTRY WITH GIVEN REP_ID
INSERT INTO AlkReferencia (id,rep_id,csatolva_id,referencia)
VALUES(null,attachToRepID,attachTo,referenciaText);
SET insid = LAST_INSERT_ID();
END
Edit.:
Yeah, the last update is useless here, should just set the rep_id while inserting, but it is not the issue here. (Copied from one of my other procedure, when I need the last update...)..Fixed in the question.
Table:
-- -----------------------------------------------------
-- Table `AlkatreszDb`.`AlkReferencia`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `AlkatreszDb`.`AlkReferencia` ;
CREATE TABLE IF NOT EXISTS `AlkatreszDb`.`AlkReferencia` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`rep_id` INT UNSIGNED NULL,
`csatolva_id` INT UNSIGNED NULL,
`referencia` VARCHAR(45) NOT NULL,
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL,
PRIMARY KEY (`id`),
INDEX `fk_AlkReferencia_AlkReferencia1_idx` (`rep_id` ASC),
INDEX `fk_AlkReferencia_AlkReferencia2_idx` (`csatolva_id` ASC),
CONSTRAINT `fk_AlkReferencia_AlkReferencia1`
FOREIGN KEY (`rep_id`)
REFERENCES `AlkatreszDb`.`AlkReferencia` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_AlkReferencia_AlkReferencia2`
FOREIGN KEY (`csatolva_id`)
REFERENCES `AlkatreszDb`.`AlkReferencia` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
This is unlikely to fix your problem, but you can simplify the code to:
INSERT INTO AlkReferencia(rep_id, csatolva_id, referencia)
SELECT ar.id, attachTo, referenciaText
FROM AlkReferencia ar
WHERE ar.id = attachTo;
I would also prepend the variable names with v_ (or something) to distinguish them from column names. That is often a source of errors in stored procedures:
INSERT INTO AlkReferencia(rep_id, csatolva_id, referencia)
SELECT ar.id, v_attachTo, v_referenciaText
FROM AlkReferencia ar
WHERE ar.id = v_attachTo;
EDIT:
This code is quite suspicious:
SELECT id
INTO attachToRepID
FROM AlkReferencia
WHERE id = attachTo;
Why not just use attachTo? Do you intend for one of the references to really be csatolva_id?
You say that your code is outputting nothing. Have you tried EXEC the procedure with parameters?
This trigger is supposed to update the free rooms available for a hospital.When a patient leaves the trigger adds one more free room.But it just doesn't work and I really cannot understand why.I use these tables for this trigger:
create table Incidents
(primary key(incident_code),
foreign key(patient_code) references Patients(patient_code)on delete cascade on update cascade,
foreign key(section_code) references Sections(section_code)on delete cascade on update cascade,
foreign key(doctor_code) references Doctors(doctor_code)on delete cascade on update cascade,
incident_code int unsigned,
patient_code int unsigned,
section_code int unsigned,
doctor_code int unsigned,
import_day date not null,
export_day date,
incident_value real not null
check(datediff(export_day,import_day)>-1));
(primary key(section_code),
section_code int unsigned,
section_name varchar(30) not null,
total_rooms int not null,
free_rooms int not null
check(total_rooms>=free_rooms));
this is the triggers code:
delimiter #
create trigger FreeRooms1
after update on incidents
for each row
begin
if (old.export_day=null and new.export_day != null)
then
update sections
set free_rooms = free_rooms + 1
where sections.section_code = incidents.section_code;
end if;
end;
#
And this is how I try to activate it:
update incidents
set export_day = '2013/12/24'
where incident_code = 2;
select section_code,section_name,free_rooms
from sections;
I tried many variations for the trigger but any change I make doesn't make work.Any help?
Sorry if my question seems bad I am fairly new to mySQL and I can't find an answer anywhere.
I think you should use:
delimiter #
create trigger FreeRooms1
after update on incidents
for each row
begin
if (old.export_day IS NULL and new.export_day IS NOT NULL)
then
update sections
set free_rooms = free_rooms + 1
where sections.section_code = NEW.section_code;
end if;
end;
#
and reference the new row.
I have multiple tables in this database; two of which are involved with this trigger
create table shipment_item(
shipmentID int not null,
shipmentItemID int not null,
purchaseID int not null,
insuredValue decimal(5,2) not null,
constraint shipment_ItemPK primary key(shipmentID, shipmentItemID),
constraint shipmentFK foreign key(shipmentID)
references shipment(shipmentID)
on delete cascade,
constraint purchaseFK foreign key(purchaseID)
references purchase(purchaseID)
);
create table purchase(
purchaseID int not null auto_increment,
storeID int not null,
purchaseDate date not null,
description char(30) not null,
category char(30) not null,
price decimal(5,2) not null,
constraint purchasePK primary key(purchaseID),
constraint storeFK foreign key(storeID)
references store(storeID)
);
I'm trying to implement a trigger in my MySQL database. That trigger looks like this
DELIMITER //
CREATE TRIGGER checkInsuranceTrigger
BEFORE INSERT ON shipment_item
FOR EACH ROW BEGIN
IF(shipment_item.insuredValue <= purchase.price) THEN
SET NEW.insuredValue = purchase.price;
END IF;
END
//
DELIMITER ;
When I implement this trigger and then try to insert data into the shipment_item table I get the following error
Error Code 1109: Unknown Table 'shipment_item' in field list
Reference the column in the row being inserted with the NEW keyword, like you did on the SET statement.
To reference values from rows in other tables, you need a SQL statement, in your case, looks like you want a SELECT.
For example (following the outline of the logic in your trigger), something like this:
BEGIN
-- local variable
DECLARE ln_purchase_price DECIMAL(5,2);
-- populate local variable (this is just an example of one way to do this)
SELECT p.price
INTO ln_purchase_price
FROM purchase p
WHERE p.purchaseID = NEW.purchaseID
LIMIT 1;
-- compare value from row to local variable
IF (NEW.insuredValue <= ln_purchase_price) THEN
SET NEW.insuredValue = ln_purchase_price;
END IF;
May I suggest verifying that the table really exists in the same database as the trigger itself?
For example I have a table which is used for logging. So very old data is useless and there are no reasons to leave it in the table. I want create a trigger which will delete old rows if number of existing rows more than 10 for example. What I already have:
CREATE TABLE log (
logId INT UNSIGNED NOT NULL AUTO_INCREMENT,
firstLogin DATETIME NOT NULL,
lastLogin DATETIME NOT NULL,
fingerprint VARCHAR(64) CHARACTER SET BINARY,
ip VARCHAR(24) NOT NULL,
accountId INT UNSIGNED NOT NULL,
FOREIGN KEY (accountId)
REFERENCES accounts (accountId)
ON UPDATE CASCADE ON DELETE CASCADE,
PRIMARY KEY (logId)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
DELIMITER |
CREATE TRIGGER logbeforeinsert BEFORE INSERT ON log
FOR EACH ROW
BEGIN
SET #rowcount = (SELECT COUNT(*) FROM log WHERE accountId = NEW.accountId);
IF #rowcount > 9 THEN
DELETE FROM log WHERE accountId = NEW.accountId LIMIT 1;
END IF;
END;
|
DELIMITER ;
But with this trigger inserting stopped at all after number of rows had reached 10.
Your trigger tries to write to the same table (DELETE is write access), that it is inserting into - this is not supported.
As you have a BEFORE INSERT trigger, failure of the trigger means failure of the INSERT.
You need either to trigger the delete from an operation, that does not write to log, or rethink your model.