Use a trigger like assertion - mysql

I know that in MySQL we can't use assertion. I Have these tables:
CREATE TABLE `soggiorno` (
`idSoggiorno` varchar(16) NOT NULL,
`price` int(11) NOT NULL,
PRIMARY KEY (`idSoggiorno`))
CREATE TABLE `prenotazione` (
`idPrenotazione` varchar(16) NOT NULL,
`soggiorno` varchar(16) NOT NULL,
`paymentType` varchar(45) NOT NULL,
PRIMARY KEY (`idPrenotazione`))
CONSTRAINT `guest_ibfk_1` FOREIGN KEY (`soggiorno`) REFERENCES `soggiorno` (`idSoggiorno`)
I have to ensure that, if 'price' > 1500, you can't pay with "cash". How can I do that without assertion? I'm thinking for a trigger...Thanks to all

Try
CREATE TRIGGER tg_prenotazione_before_insert
BEFORE INSERT ON prenotazione
FOR EACH ROW
SET NEW.soggiorno = IF((SELECT price
FROM soggiorno
WHERE idSoggiorno = NEW.soggiorno) > 1500
AND NEW.paymentType = 'Cash', NULL, NEW.soggiorno);
What is does it checks for your desired conditions (if price > 1500 and paymentType is 'Cash') and if it's a match it violates NOT NULL constraint effectively preventing a row from being inserted.
Here is SQLFiddle demo. Try to uncomment the last insert statement in the demo and you'll see that it won't let you insert it.

Related

Creating a MySQL trigger to set a boolean value to true if a collection of booleans are true

I am creating an inventory management app in node.js that uses MySQL as a database. I have a weak entity “rental_item” that holds the items in a particualr rental. The issue is that the rental may not come back all at once so I need a way of marking the “rental_returned” boolean in the rental table true only when all of the “item_returned” entires are true.
Here is my table structure:
CREATE TABLE `rental` (
`rental_id` int NOT NULL AUTO_INCREMENT,
`renter_id` int NOT NULL,
`date_in` date NOT NULL,
`date_out` date NOT NULL,
`sig_path` varchar(50) NOT NULL,
`doc_path` varchar(50) NOT NULL,
`col_name` varchar(50) NOT NULL,
`col_path` varchar(50) NOT NULL,
`cost` decimal(15,2) NOT NULL,
`rental_returned` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`rental_id`),
UNIQUE KEY `doc_path` (`doc_path`),
UNIQUE KEY `col_path` (`col_path`),
UNIQUE KEY `sig_path` (`sig_path`),
KEY `renter_key` (`renter_id`),
CONSTRAINT `renter_key` FOREIGN KEY (`renter_id`) REFERENCES `renter` (`renter_id`)
)
CREATE TABLE `rental_item` (
`rental_id` int NOT NULL,
`i_ID` varchar(20) NOT NULL,
`item_returned` tinyint(1) NOT NULL DEFAULT '0',
KEY `rental_key` (`rental_id`),
KEY `rental_item_key` (`i_ID`),
CONSTRAINT `rental_item_key` FOREIGN KEY (`i_ID`) REFERENCES `item` (`i_ID`),
CONSTRAINT `rental_key` FOREIGN KEY (`rental_id`) REFERENCES `rental` (`rental_id`) ON DELETE CASCADE
)
I am currently doing this through the mysql2 node.js module and just checking for all the values of a given rental_id. I then found out about triggers and thought this way could be better. I fiddled round with things like this Trigger with table join, but couldn’t wrap my head around how to get the rental_id of the entry that was updated from rental_item, then check that all entires in rental_item with that id have item_returned = 1, and finally update the rental table to show that all the items/the complete rental has been returned.
I understand that this sould be an update after trigger on rental_item but dont know how to handle the conditionals or loops needed.
Use NEW.rental_id to get the ID of the row that was updated.
CREATE TRIGGER rental_returned AFTER UPDATE ON rental_item
FOR EACH ROW
UPDATE rental
SET rental_returned = (
NOT EXISTS (
SELECT *
FROM rental_item
WHERE rental_id = NEW.rental_id
AND item_returned = 0))
WHERE rental_id = NEW.rental_id

create a trigger that adds a field to +5 and creates the total in another table

I'm new and I'm not so good with the triggers, I place the tables and figure out how to create this sum
-- Table `databasestreaming`.`tipologiaabbonamento`
CREATE TABLE IF NOT EXISTS `databasestreaming`.`tipologiaabbonamento` (
`idtipologiaabbonamento` INT NOT NULL AUTO_INCREMENT,
`piano abbonamento` VARCHAR(45) NULL,
`dispositivi` VARCHAR(45) NULL,
`Risoluzione` VARCHAR(45) NULL,
`Prezzo` INT NULL,
PRIMARY KEY (`idtipologiaabbonamento`))
ENGINE = InnoDB;
-- Table databasestreaming.abbonamento
CREATE TABLE IF NOT EXISTS `databasestreaming`.`abbonamento` (
`idabbonamento` INT NOT NULL AUTO_INCREMENT,
`data attivazione` DATE NULL,
`data termine` DATE NULL,
`Sport` ENUM("Si", "No") NULL,
`cliente_idcliente` INT NOT NULL,
`tipologiaabbonamento_idtipologiaabbonamento` INT NOT NULL,
`Prezzo_finale` INT NULL,
PRIMARY KEY (`idabbonamento`, `cliente_idcliente`, `tipologiaabbonamento_idtipologiaabbonamento`),
INDEX `fk_abbonamento_cliente_idx` (`cliente_idcliente` ASC) ,
INDEX `fk_abbonamento_tipologiaabbonamento1_idx` (`tipologiaabbonamento_idtipologiaabbonamento` ASC) ,
CONSTRAINT `fk_abbonamento_cliente`
FOREIGN KEY (`cliente_idcliente`)
REFERENCES `databasestreaming`.`cliente` (`idcliente`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_abbonamento_tipologiaabbonamento1`
FOREIGN KEY (`tipologiaabbonamento_idtipologiaabbonamento`)
REFERENCES `databasestreaming`.`tipologiaabbonamento` (`idtipologiaabbonamento`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
I would like that when the user selects from enum Si with Sport, the trigger will take the price from the type of subscription (three types of subscriptions that will choose the user) and add the type of subscription chosen to the eventual or not of the sport choice, if yes, then we add +5 if instead it chooses no, in the final price I would like it to simply report the price of the chosen subscription

SQL statement to limit an attribute

I have 3 tables REVIEW,PAPER and PCMEMBER and the codes for it is as mentioned below:
CREATE TABLE REVIEW(
due_date DATE NOT NULL,
review_date DATE NOT NULL,
recommendation VARCHAR(50) NOT NULL,
comment VARCHAR(50) NOT NULL,
pcmem_id NUMBER(10) NOT NULL,
paper_id NUMBER(10) NOT NULL,
CONSTRAINT review_pk PRIMARY KEY (pcmem_id,paper_id),
CONSTRAINT review_fk FOREIGN KEY(paper_id)
REFERENCES PAPER(paper_id),
CONSTRAINT review_fk1 FOREIGN KEY(pcmem_id)
REFERENCES PCMEMBER(pcmem_id));
CREATE TABLE PCMEMBER(
pcmem_id NUMBER(10) NOT NULL PRIMARY KEY,
pc_fname VARCHAR(20) NOT NULL,
pc_sname VARCHAR(20) NOT NULL,
pc_title VARCHAR (20) NOT NULL,
pc_position VARCHAR(20) NOT NULL,
affiliation VARCHAR(20) NOT NULL,
pc_email VARCHAR(20) NOT NULL,
track_id NUMBER(6) NOT NULL,
CONSTRAINT pcmember_fk FOREIGN KEY(track_id)
REFERENCES TRACK(track_id));
CREATE TABLE PAPER(
paper_id NUMBER(10) PRIMARY KEY NOT NULL,
paper_title VARCHAR(20) NOT NULL,
abstract VARCHAR(50) NOT NULL,
paper_type VARCHAR(20) NOT NULL,
submission_date DATE NOT NULL,
track_id NUMBER(6) NOT NULL,
CONSTRAINT paper_fk FOREIGN KEY(track_id)
REFERENCES TRACK(track_id),
CONSTRAINT chk_type CHECK(paper_type IN ('full paper','Research-in-Progress','posters')),
);
I'm trying to add a condition where "Each paper will be reviewed by exactly 3 PC members". Not sure what CHECK constraints I should be using? I just need this for creating tables. Thanks
One way you could implement this type of check constraint is with a scalar function that performs the check.
The following function, given a paper id, will check whether the paper is reviewed by 3 members only and it returns 1 if the condition is satisfied and 0 otherwise.
CREATE FUNCTION isReviewdByThreeMembers (
#paperID NUMBER(10)
)
RETURNS INTEGER
AS
BEGIN
DECLARE #count INTEGER
SELECT #count = COUNT(*)
FROM Review
WHERE paper_id = #paperID
RETURN CASE
WHEN #count = 3 THEN 1
ELSE 0
END
END
And then use this function in your check constraint like so:
CHECK (isReviewdByThreeMembers(paper_id) = 1)
You mentioned you want each paper to "only be reviewed by 3 members" but this condition can never be satisfied by records that don't already exist because each new paper would initially start with no reviews and incrementally get new reviews until it gets to 3.
If you were to modify the constraint to allow each paper to be reviewed by "up to 3 reviewers" then the constraint would actually be a bit more useful. Perhaps in conjunction with another constraint that would maybe prevent the status of a paper from transitioning into "REVIEWED" or "COMPLETED" once the 3 required reviews are completed.

insert query is not working for the row which has a foreign key

I am trying to learn foreign key constraint so far i have been able to create foreign keys in mysql
Here is my create table query for three tables:
create table customer(
CustId int(100) not null AUTO_INCREMENT primary key,
FirstName varchar(300) default null,
LastName varchar(300) default null,
Gender varchar(200) default null,
Category varchar(200) default null,
DateOfBirth varchar(200) default null,
Age int(3)default null
);
create table address(
Address_Id int(100) not null AUTO_INCREMENT primary key,
Address varchar(1000) default null,
Country varchar(500) default null,
State varchar (500) default null,
city varchar(500)default null,
PinCode int(10)default null,
CustId int(100) not null,
foreign key(CustId) references customer(CustId)
);
create table contact(
Contact_Id int(100) not null AUTO_INCREMENT primary key,
EmailId varchar(500)default null,
ContactNo varchar(20) default null,
MobileNo varchar(20) default null,
CustId int(100) not null,
Address_Id int(100) not null,
foreign key(CustId) references customer(CustId),
foreign key(Address_Id) references address(Address_Id)
);
K now i got it till here :
START TRANSACTION;
SET #lid := null;
insert into customer (FirstName,LastName,Gender,Category,DateOfBirth,Age)values('Ashok','sharma','Male','Affiliate','1988-04-17','26');
SELECT LAST_INSERT_ID() INTO #lid;
insert into address(CustId, Address,Country,State,city,Pincode)values (#lid, 'No.1645','India','Karnataka','Bangalore','560060');
COMMIT;
Using transaction and commit was the solution but how to do it for contact table where i have too foreign keys. And i need to get two auto incremented values.
Please guide me on this as well.
You do this in multiple statements:
START TRANSACTION;
SET #lid := null;
insert into customer (FirstName,LastName,Gender,Category,DateOfBirth,Age)values('Ashok','sharma','Male','Affiliate','1988-04-17','26');
SELECT LAST_INSERT_ID() INTO #lid;
insert into address(CustId, Address,Country,State,city,Pincode)values (#lid, 'No.1645','India','Karnataka','Bangalore','560060');
COMMIT;
For MySQL 5.1.12 and later, with no argument, LAST_INSERT_ID() returns a 64-bit value representing the first automatically generated value successfully inserted for an AUTO_INCREMENT column as a result of the most recently executed INSERT statement.
This is session bound, so don't worry, that another session messes up your last_insert_id or something. Read more about it here.
And you better put it all in a transaction like I did. This makes sure, that all statements succeed or none, not just parts of them. You have to use InnoDB for it though. MyISAM does not support this. Or you live with the risk :) But since you use foreign keys I assume you use InnoDB, just wanted to mention it for completeness.
The variable I used can of course be replaced with a PHP variable. Or you do it like this:
START TRANSACTION;
insert into customer (FirstName,LastName,Gender,Category,DateOfBirth,Age)values('Ashok','sharma','Male','Affiliate','1988-04-17','26');
SELECT LAST_INSERT_ID() INTO #lid;
insert into address(CustId, Address,Country,State,city,Pincode)
SELECT #lid, 'No.1645','India','Karnataka','Bangalore','560060';
COMMIT;
EDIT:
START TRANSACTION;
SET #lid_c := null;
SET #lid_a := null;
insert into customer (FirstName,LastName,Gender,Category,DateOfBirth,Age)values('Ashok','sharma','Male','Affiliate','1988-04-17','26');
SELECT LAST_INSERT_ID() INTO #lid_c;
insert into address(CustId, Address,Country,State,city,Pincode)values (#lid, 'No.1645','India','Karnataka','Bangalore','560060');
SELECT LAST_INSERT_ID() INTO #lid_a;
INSERT INTO contact (CustId, Address_Id, another_column) VALUES
(#lid_c, #lid_a, 'another_value');
COMMIT;

MySQL: update a record, on duplicate key leave NEWER one, and delete OLDER one? See inside

There is a table:
CREATE TABLE `mytable` (
`user_id` INT(10) UNSIGNED NOT NULL,
`thing_id` VARCHAR(100) NOT NULL DEFAULT '',
`lock_date` DATETIME NOT NULL,
`lock_id` VARCHAR(36) NOT NULL,
PRIMARY KEY (`user_id`,`thing_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
and some values there:
INSERT INTO mytable(user_id,thing_id,lock_date,lock_id)
VALUES
(51082,'299ac9ff-2b2b-102d-8ff6-f64c971398c3','2012-03-16 00:39:12','ec7b2008-6ede-11e1-aac2-5924aae99221'),
(108325,'299ac9ff-2b2b-102d-8ff6-f64c971398c3','2013-02-05 19:30:03','7c6de986-6edd-11e1-aac2-5924aae99221'),
(108325,'d90b354d-4b5f-11e0-9959-47117d41cf4b','2012-03-16 00:47:41','1c243032-6ee0-11e1-aac2-5924aae99221');
I want to delegate all records of user_id = 108325 to user_id = 51082, and if both users have an equal thing_id field, leave the newer one only (lock_date1 > lock_date2), so that I have following result:
51082,'299ac9ff-2b2b-102d-8ff6-f64c971398c3','2013-02-05 19:30:03','7c6de986-6edd-11e1-aac2-5924aae99221'
108325,'d90b354d-4b5f-11e0-9959-47117d41cf4b','2012-03-16 00:47:41','1c243032-6ee0-11e1-aac2-5924aae99221'
Note that 51082 now has a newer record: lock_date = '2013-02-05 19:30:03' instead of '2012-03-16 00:39:12'.
So, how can I update a row, and on duplicate key leave the newer one (by some particular field)?
Thanks!
INSERT INTO
mytable(user_id,thing_id,lock_date,lock_id)
VALUES
(51082,'299ac9ff-2b2b-102d-8ff6-f64c971398c3','2012-03-16 00:39:12','ec7b2008-6ede-11e1-aac2-5924aae99221'),
(108325,'299ac9ff-2b2b-102d-8ff6-f64c971398c3','2013-02-05 19:30:03','7c6de986-6edd-11e1-aac2-5924aae99221'),
(108325,'d90b354d-4b5f-11e0-9959-47117d41cf4b','2012-03-16 00:47:41','1c243032-6ee0-11e1-aac2-5924aae99221')
ON DUPLICATE KEY UPDATE SET
user_id = VALUES(user_id),
lock_date = VALUES(lock_date),
lock_id = VALUES(lock_id)