I'm a MySQL newbie, I just discovered that it doesn't support assertions.
I got this table:
CREATE TABLE `guest` (
`ssn` varchar(16) NOT NULL,
`name` varchar(200) NOT NULL,
`surname` varchar(200) NOT NULL,
`card_number` int(11) NOT NULL,
PRIMARY KEY (`ssn`),
KEY `card_number` (`card_number`),
CONSTRAINT `guest_ibfk_1` FOREIGN KEY (`card_number`) REFERENCES `member` (`card_number`)
)
What I need is that a member can invite maximum 2 guests.
So, in table guest I need that a specific card_number can appear maximum 2 times.
How can I manage it without assertions?
Thanks.
This definitly smells of a BEFORE INSERT trigger on the table 'guest':
DELIMITER $$
DROP TRIGGER IF EXISTS check_guest_count $$
CREATE TRIGGER check_guest_count BEFORE INSERT ON `guest`
FOR EACH ROW BEGIN
DECLARE numguests int DEFAULT 0;
SELECT COUNT(*) INTO numguests FROM `guest` WHERE card_number=NEW.card_number;
if numguests>=2 THEN
SET NEW.card_number = NULL;
END IF;
END;
$$
DELIMITER ;
This basically looks up the current guest count, and if it is already >=2 sets card_number to NULL. Since card_number is declared NOT NULL, this will reject the insert.
Tested and works for me on MySQL 5.1.41-3ubuntu12.10 (Ubuntu Lucid)
Related
I've a table tbl_bonus. I want to update the status of bonus when point==used_point. Want to update the status to used at that time. otherwise the status will remain unused.
This is the table.
CREATE TABLE `tbl_bonus` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`point` INT(5) NOT NULL,
`used_point` INT(5) DEFAULT NULL,
`status` ENUM('unused','used') NOT NULL,
PRIMARY KEY (`id`),
);
This is the trigger I'm trying to run.
DELIMITER $$
CREATE TRIGGER xyz BEFORE UPDATE ON tbl_bonus FOR EACH ROW
BEGIN
IF (new.used_point <=> new.point) THEN
SET new.status = 'used':
END IF:
END $$
DELIMITER :
How shall I write the trigger that will suffice my requirement? Will appreciate any sort of help.
I have a field called currentItem , this field has a trigger that calls a stored procedure (with transaction) sp_TransferData to perform some transfers of information to a worktable. If the stored procedure fails - I would like to restore the old value of currentItem - as it has not effectively changed.
I am using MySQL and I would like this logic to be in my trigger - obviously we do not want an endless loop so how do I accomplish this ?
Trigger Pseudo Code :
Call sp_TransferData(#ResultCode);
Select #ResultCode into Result;
If Result < 0 THEN
thisField Value = OLD.Value;
End If;
// EDIT 9-16-2016 13:00 //
There is only 1 Row in this table never any thing more! Column list edited for brevity.
table global_items
Id INT PK,
lsize INT,
wsize INT,
currentItem INT
Trigger is on currentItem After Update or Before I do not care which as long as it works and does not refire the trigger:
If the value has changed CALL sp_TransferData(#ResultCode);
If (SELECT #ResultCode) < 0 THEN
Reset currentItem to old value but do not cycle the trigger since we are only resetting it.
EndIf;
Just to add this is what I have in my trigger code which is not correct.
The Table definitions are supplied.
BEGIN
IF NEW.currentItem <> OLD.currentItem THEN
call sp_CurrentItemChanged(NEW.currentItem, #ResultCode, #ResultMsg);
IF ((Select #ResultCode) < 0) THEN
NEW.currentItem = OLD.currentItem;
END IF;
END IF;
END
CREATE TABLE working_table (
Id int(11) NOT NULL AUTO_INCREMENT,
Mbf float DEFAULT NULL,
Width float DEFAULT NULL,
Pulse int(11) DEFAULT NULL,
PRIMARY KEY (Id)
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;
CREATE TABLE recipe (
Id int(11) NOT NULL AUTO_INCREMENT,
Name varchar(80) NOT NULL DEFAULT 'UnAssigned',
IsDefault tinyint(1) DEFAULT 0,
PRIMARY KEY (Id),
UNIQUE INDEX Id_UNIQUE (Id),
UNIQUE INDEX Name_UNIQUE (Name)
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;
CREATE TABLE Packs (
Id int(11) NOT NULL AUTO_INCREMENT,
Name varchar(45) NOT NULL DEFAULT 'UNDEFINED',
Width float NOT NULL DEFAULT 0,
Pulse int(11) NOT NULL DEFAULT 0,
Mbf float NOT NULL DEFAULT 0,
RecipeID int(11) NOT NULL DEFAULT 0,
SetupID int(11) DEFAULT 1,
PRIMARY KEY (Id),
INDEX SetupID_ndx (SetupID),
INDEX FK_PackRecipeID_Recipe_ID_idx (RecipeID),
INDEX FK_RecipeID_PackS_idx (RecipeID),
CONSTRAINT FK_PackRecipeID_Recipe_ID FOREIGN KEY (RecipeID)
REFERENCES recipe (Id) ON DELETE CASCADE ON UPDATE CASCADE
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;
CREATE TABLE global_items (
Id int(11) NOT NULL AUTO_INCREMENT,
PackSys_Count int(11) DEFAULT NULL,
Active_Recipe int(11) DEFAULT 1,
PRIMARY KEY (Id)
)
ENGINE = INNODB
AUTO_INCREMENT = 2
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;
When global_items.Active_recipe changes the trigger fires .. The data that is moved is in packs and its related tables (brevity here) to the working_table. The global_items table is NEVER touched by anything in the lengthy Stored Procedure or any other triggers or any other sql code. It is never modified by anything internal to the SQL storage - it is touched only by an outside applications. I am not sure how I restore the value to the original value on failure of the stored procedure.
I think I might be understanding what you are getting at. But since you did not show your trigger entirely I guess, and not the Stored Proc, I just winged it and investigated.
I think the problem is that your stored proc does not have an OUT qualifier to it to make it write-able. The below works fine and I think it captures how to solve your issue.
Schema:
-- drop table global_items;
create table global_items
( Id INT primary key,
lsize INT not null,
wsize INT not null,
currentItem INT not null,
theCount int not null
);
insert global_items(Id,lsize,wsize,currentItem,theCount) VALUES
(1,1,1,100,0);
select * from global_items;
Trigger:
DROP TRIGGER IF EXISTS giBeforeUpdate;
DELIMITER $$
CREATE TRIGGER giBeforeUpdate
BEFORE UPDATE
ON global_items FOR EACH ROW
BEGIN
DECLARE tRet INT;
SET tRet=0;
SET NEW.theCount=OLD.theCount+1;
CALL uspDoSomething7(tRet);
IF tRet=1 THEN
-- stored proc said FAILURE
SET NEW.currentItem=OLD.currentItem;
END IF;
END;$$
DELIMITER ;
Stored Procedure:
DROP PROCEDURE IF EXISTS uspDoSomething7;
DELIMITER $$
CREATE PROCEDURE uspDoSomething7(OUT retVal INT)
BEGIN
DECLARE rndNum INT;
SET rndNum=FLOOR(RAND()*2)+1; -- random number 1 or 2
-- sets retVal to 1 on FAILURE
IF rndNum=2 THEN
SET retVal=1; -- FAIL
ELSE
SET retVal=0; -- SUCCESS
END IF;
END$$
DELIMITER ;
Test:
Repeatedly call this confirm that it fails roughly half the time
that is, currentItem retains its old value
but chg the update stmt below each time for the currentItem= part of it
update global_items set currentItem=410,lsize=2 where Id=1;
select * from global_items;
So, I have this one column in my table that gets filled by a trigger when a new entry is inserted.
CREATE TABLE `users` (
`idusers` int(11) NOT NULL AUTO_INCREMENT,
`uid` char(64) DEFAULT NULL,
`uname` varchar(80) NOT NULL,
`password` char(128) NOT NULL,
`mail` varchar(120) NOT NULL,
PRIMARY KEY (`idusers`),
UNIQUE KEY `uname_UNIQUE` (`uname`),
UNIQUE KEY `mail_UNIQUE` (`mail`),
UNIQUE KEY `uid_UNIQUE` (`uid`)
);
DELIMITER $$
TRIGGER `flask`.`users_BEFORE_INSERT` BEFORE INSERT ON `users` FOR EACH ROW
BEGIN
set new.uid = sha2(new.idusers, 256);
END$$
DELIMITER ;
The problem now is that when I try to add a new row (only have a test one yet because of the error), in the trigger the value of new.idusers is somehow always 0 instead of the current auto_increment value.
What do I need to change in my trigger code so that the value used for generating the uid is the actual id and not always 0?
Since idusers is an AUTO_INCREMENT field, its value is known only after the record is inserted in the table.
Use an AFTER INSERT trigger instead of a BEFORE INSERT trigger, and update the newly inserted record:
DELIMITER $$
CREATE TRIGGER `flask`.`users_AFTER_INSERT` AFTER INSERT ON `users` FOR EACH ROW
BEGIN
UPDATE users SET uid = sha2(NEW.idusers, 256) WHERE idusers = NEW.idusers;
END $$
DELIMITER ;
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?
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?