I created my ER and all my tables in MySQL but I need to know how to set a max number allowed for one of my attributes.
The attribute is a vehicle and I want my database to set a limit to 10 and not allow the user to add an 11th car.
I'm not too familiar with triggers. How can I start?
Why don't you just count the number of rows in your table before inserting?
If the count return 10, don't insert.
Mysql doesn't provide a mechanims to prevent a INSERT INTO a table with triggers.
A solution is use a trigger with a not null column.
CREATE TRIGGER carsCheck BEFORE INSERT ON cars
for each row
begin
DECLARE counter INT;
SET counter = (select count(id) from cars);
if counter > 10 then
NEW.notNulCol = null;
end if;
end;
Related
I want to add a limit to how many items my table can have. I want the maximum amount of items to be 10. I want it to only be 10 people in my table. I dont want it to be able to add items after the 10th person. Here is my code:
CREATE TABLE person (
name VARCHAR(233) NOT NULL,
number int NOT NULL AUTO_INCREMENT,
PRIMARY KEY(number),
Check(number>10))
delimiter //
create trigger limit_persons before insert on person for each row
begin
declare count integer
select COUNT(*) FROM person INTO count;
if count>=10 then
signal sqlstate '45000' set message_text = 'Limit exceeded';
end if;
end
//
I would advise to handle such stuff as limitations in the software itself. So you have control over it later and it is overall a cleaner solution. But you can try this, if you really want to limit it in mysql:
ALTER TABLE tbl_name MAX_ROWS=10 AVG_ROW_LENGTH=nnn;
You can also check out triggers and signals:
https://dev.mysql.com/doc/refman/5.5/en/signal.html
https://dev.mysql.com/doc/refman/5.5/en/trigger-syntax.html
You can set up a trigger (to be specific, an Insert trigger) that counts the records and, if count is more than 10, it does not allow the insert operation.
Following code will be helpful to you,
DELIMITER $$
CREATE TRIGGER LimitRowCountTrigger
BEFORE INSERT
ON person
FOR EACH ROW
BEGIN
SELECT COUNT(*) INTO #cnt FROM person;
IF #cnt > 10 THEN
CALL sth(); -- raise an error
END IF;
END
$$
DELIMITER ;
How can I make trigger to read from two separated attribute in two different tables and then
calculate something .
this is the code I want to make but the mysql says it has problem
CREATE TRIGGER `fee_calculate` BEFORE INSERT ON `BookingRoom`
FOR EACH ROW BEGIN
SET NEW.fee = `Booking.Night_Qty` * `Room.RoomPrice` ;
END
I Think It Should Help You
I made some assumptions:
I assumed you wanted to update the reorders table with a productID that matches the Products table. (you can do an insert but may run into PK issues depending on how you have your table set up).
I assumed that the reorder amount will be placed there.
I did NOT include transaction statements, I would recommend adding these so it's logged.
CREATE trigger updateprod
on Products
for update as
begin
declare #qtyonhand int,
#reorder int
if update(QtyonHand)
begin
select #qtyonhand = QtyonHand,#reorder = ReorderAmount from inserted
if #qtyonhand < #reorder
begin
update Reorders set ReorderAmount = #reorder, ReorderDate = getdate()
end
else if #qtyonhand >= #reorder
begin
delete from reorders where ProductID = ProductID
end
end
end
I have created this trigger to update the seq column. I have to keep track of the order of certain items in the table, but only if the liability_category_id = 1,2. So my ordering is tricky because any item with a liability_category_id = 3 I don't need to track.
In my trigger, I'm querying to find the last entered seq number (using max(seq)), then turning around and updating the new entry with the seq + 1.
DELIMITER $$
USE `analysisdb`$$
DROP TRIGGER /*!50032 IF EXISTS */ `trigger_liability_detail_after_insert`$$
CREATE
/*!50017 DEFINER = 'admin'#'%' */
TRIGGER `trigger_liability_detail_after_insert` AFTER INSERT ON `liability_detail`
FOR EACH ROW BEGIN
DECLARE SortOrder INT;
IF NEW.liability_category_id = 1 OR NEW.liability_category_id = 2 THEN
SET SortOrder = (SELECT MAX(seq) FROM liability_detail WHERE analysis_id = new.analysis_id AND liability_category_id IN (1, 2));
UPDATE liability_detail SET seq = (SortOrder + 1) WHERE id = NEW.id;
END IF;
END;
$$
DELIMITER ;
However, when entering a new item, I get this error: Can't update table 'liability_detail' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
Is there a better way to control the ordering of these items? My original thought was to simply set the first seq = 1, then seq = 2, etc. The ordering is reset for each new analysis_id though.
I think the workaround is to make this a before trigger and update the record being insert itself prior to insert.
So
CREATE
/*!50017 DEFINER = 'admin'#'%' */
TRIGGER `trigger_liability_detail_after_insert` BEFORE INSERT ON `liability_detail`
FOR EACH ROW BEGIN
DECLARE SortOrder INT;
IF NEW.liability_category_id = 1 OR NEW.liability_category_id = 2 THEN
SET NEW.seq = 1 + IFNULL((SELECT MAX(seq) FROM liability_detail WHERE analysis_id = new.analysis_id AND liability_category_id IN (1, 2)), 0);
END IF;
END;
$$
That was a quick copy/paste, but it should be something along those lines.
That's going to be tricky to handle.
The easy answer is if this could be changed to a BEFORE INSERT FOR EACH ROW trigger,
then you could:
SET NEW.seq = (SortOrder + 1);
to set the value on the row BEFORE it gets inserted into the table. But you can't do that in an AFTER INSERT FOR EACH ROW trigger.
There are some performance and concurrency concerns with using a trigger. (You don't have any guarantee that you won't be generating a "duplicate" value for the seq column when concurrent inserts are running; but that may not be a show stopper issue for you.)
I would prefer the approach of using a simple AUTO_INCREMENT column for the whole table.
The values from that would be "in order" for all the rows, so a query like
... WHERE liability_category_id = 1 ORDER BY seq
would return rows "in the order" those rows were inserted. There would be "gaps" in the sequence number for a given liability_category_id, but the sequence (order) of the inserts would be preserved.
(NOTE: MyISAM has a nifty feature of an AUTO_INCREMENT column, let's it "increment" separately for different values of a leading column in an index. But that only works in the MyISAM engine, it doesn't work in InnoDB.)
Aside from an AUTO_INCREMENT column, I would also consider a TIMESTAMP DEFAULT CURRENT_TIMESTAMP column to record the date/time when the row is inserted.
... WHERE liability_category_id = 1 ORDER BY timestamp_default_current ASC
Both of those approaches are simple column definitions, and do not require any procedural code to be written or maintained.
I have an issue with a trigger on a mysql database. I have a table such as follows:
id int not null auto_increment (PK)
parent_id int not null,
rank int not null
What I'm trying to do is use a trigger to update the rank to the next highest +10 when they have the same parent_id, but this doesn't seem to be working.
DELIMITER $$
DROP TRIGGER IF EXISTS after_insert $$
create trigger after_insert
after insert on mytable
FOR EACH row
BEGIN
IF EXISTS (SELECT rank FROM mytable WHERE parent_id = new.parent_id AND id != new.id ORDER BY rank DESC LIMIT 1) THEN
UPDATE mytable SET rank = 10
WHERE id = new.id;
ELSE
UPDATE mytable SET rank = 20
WHERE id = new.id;
END IF;
END
$$
I've tried setting the new rank to a variable and calling the update statement using that, and again it didn't work. I even created another table to log what values were being selected and that worked perfectly so I can't quite understand what's going on. Is it a case of, although the trigger is "AFTER INSERT" the insert hasn't actually happened so it can't update the row it's just inserted? Another reason I ask this is, I've even tried updating the rank to different values e.g 1 and 2 depending on which statement it goes to, but it always ends up being 0.
I think you're on the right track with this thought:
Is it a case of, although the trigger is "AFTER INSERT" the insert hasn't actually happened so it can't update the row it's just inserted?
From the FAQ:
B.5.9: Can triggers access tables?
A trigger can access both old and new data in its own table. A trigger can also affect other tables, but it is not permitted to modify a table that is already being used (for reading or writing) by the statement that invoked the function or trigger.
The documentation isn't clear that what you're doing won't work. OTOH, the documentation isn't clear that what you're trying to do will work either.
I think you'd be better off using a BEFORE INSERT trigger and setting NEW.rank in there. Then, the new row would have the right rank value when it is actually inserted into the table rather than patching it after. Also, you'd be able to simplify your existence check to just this:
EXISTS(SELECT rank FROM mytable WHERE parent_id = new.parent_id)
as NEW.id wouldn't have a useful value and the new row wouldn't be in the table anyway; the ORDER BY and LIMIT are also unnecessary as you're just checking if something exists so I took them out.
A BEFORE INSERT trigger seems to match your intent better anyway and that will give you correct data as soon as it is inserted into your table.
If you want the rank to be set +10 more than highest "brother's" rank, you could use:
DELIMITER $$
DROP TRIGGER IF EXISTS whatever $$
create trigger whatever
BEFORE INSERT ON mytable
FOR EACH row
BEGIN
SET NEW.rank = 10 + COALESCE(
( SELECT max(rank)
FROM mytable
WHERE parent_id = NEW.parent_id
), 0 ) ;
END
$$
I have a trigger in which I want to have a variable that holds an INT I get from a SELECT, so I can use it in two IF statements instead of calling the SELECT twice. How do you declare/use variables in MySQL triggers?
You can declare local variables in MySQL triggers, with the DECLARE syntax.
Here's an example:
DROP TABLE IF EXISTS foo;
CREATE TABLE FOO (
i SERIAL PRIMARY KEY
);
DELIMITER //
DROP TRIGGER IF EXISTS bar //
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = NEW.i;
SET #a = x; -- set user variable outside trigger
END//
DELIMITER ;
SET #a = 0;
SELECT #a; -- returns 0
INSERT INTO foo () VALUES ();
SELECT #a; -- returns 1, the value it got during the trigger
When you assign a value to a variable, you must ensure that the query returns only a single value, not a set of rows or a set of columns. For instance, if your query returns a single value in practice, it's okay but as soon as it returns more than one row, you get "ERROR 1242: Subquery returns more than 1 row".
You can use LIMIT or MAX() to make sure that the local variable is set to a single value.
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = (SELECT age FROM users WHERE name = 'Bill');
-- ERROR 1242 if more than one row with 'Bill'
END//
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = (SELECT MAX(age) FROM users WHERE name = 'Bill');
-- OK even when more than one row with 'Bill'
END//
CREATE TRIGGER clearcamcdr AFTER INSERT ON `asteriskcdrdb`.`cdr`
FOR EACH ROW
BEGIN
SET #INC = (SELECT sip_inc FROM trunks LIMIT 1);
IF NEW.billsec >1 AND NEW.channel LIKE #INC
AND NEW.dstchannel NOT LIKE ""
THEN
insert into `asteriskcdrdb`.`filtre` (id_appel,date_appel,source,destinataire,duree,sens,commentaire,suivi)
values (NEW.id,NEW.calldate,NEW.src,NEW.dstchannel,NEW.billsec,"entrant","","");
END IF;
END$$
Dont try this # home
`CREATE TRIGGER `category_before_ins_tr` BEFORE INSERT ON `category`
FOR EACH ROW
BEGIN
**SET #tableId= (SELECT id FROM dummy LIMIT 1);**
END;`;
I'm posting this solution because I had a hard time finding what I needed. This post got me close enough (+1 for that thank you), and here is the final solution for rearranging column data before insert if the data matches a test.
Note: this is from a legacy project I inherited where:
The Unique Key is a composite of rridprefix + rrid
Before I took over there was no constraint preventing duplicate unique keys
We needed to combine two tables (one full of duplicates) into the main table which now has the constraint on the composite key (so merging fails because the gaining table won't allow the duplicates from the unclean table)
on duplicate key is less than ideal because the columns are too numerous and may change
Anyway, here is the trigger that puts any duplicate keys into a legacy column while allowing us to store the legacy, bad data (and not trigger the gaining tables composite, unique key).
BEGIN
-- prevent duplicate composite keys when merging in archive to main
SET #EXIST_COMPOSITE_KEY = (SELECT count(*) FROM patientrecords where rridprefix = NEW.rridprefix and rrid = NEW.rrid);
-- if the composite key to be introduced during merge exists, rearrange the data for insert
IF #EXIST_COMPOSITE_KEY > 0
THEN
-- set the incoming column data this way (if composite key exists)
-- the legacy duplicate rrid field will help us keep the bad data
SET NEW.legacyduperrid = NEW.rrid;
-- allow the following block to set the new rrid appropriately
SET NEW.rrid = null;
END IF;
-- legacy code tried set the rrid (race condition), now the db does it
SET NEW.rrid = (
SELECT if(NEW.rrid is null and NEW.legacyduperrid is null, IFNULL(MAX(rrid), 0) + 1, NEW.rrid)
FROM patientrecords
WHERE rridprefix = NEW.rridprefix
);
END
Or you can just include the SELECT statement in the SQL that's invoking the trigger, so its passed in as one of the columns in the trigger row(s). As long as you're certain it will infallibly return only one row (hence one value). (And, of course, it must not return a value that interacts with the logic in the trigger, but that's true in any case.)
As far I think I understood your question
I believe that u can simply declare your variable inside "DECLARE"
and then after the "begin" u can use 'select into " you variable" ' statement.
the code would look like this:
DECLARE
YourVar varchar(50);
begin
select ID into YourVar from table
where ...