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?
Related
I am having an issue with a trigger I have put in a database I am building. It is the only trigger in the database. Here are the two tables being used.
Client Table
create table client (
clientNum INT(5) not null auto_increment,
clientName TEXT(30) not null,
clientEmail VARCHAR(64) not null,
clientGender CHAR(1) not null,
clientDOB DATE not null,
clientAddress TEXT(50),
clientPhone VARCHAR(12) not null,
hasInsurance CHAR(1) not null,
clientBalanceOwed DECIMAL(10,2),
clientLastDateVisited DATE,
clientNextVisitDate DATE,
primary key (clientNum));
Insurance Table
create table insurance(
insuranceNum INT(5) not null auto_increment,
cardNum INT(16),
policyNum INT(6),
policyHolder TEXT(30),
clientNum INT(5),
primary key (insuranceNum),
foreign key (clientNum) references client(clientNum));
The idea for the following trigger is to only create an insurance row when a client is added to the database that has the 'hasInsurance' field set to 'y'. Then, once that client has been added, create a new insurance row with the clientNum set to the clientNum that was just added.
The Trigger
delimiter $$
create trigger New_Insurance_Row after insert on client
for each row
begin
if(client.hasInsurance = 'y') then
insert into insurance (clientNum) values (NEW.clientNum);
end if;
end$$
Everything up to this point works as intended, until you try to insert a new client into the table and call the trigger. Once I try and add the following line of code:
The Insert Statement
insert into client(clientName, clientEmail, clientGender, clientDOB,
clientAddress,
clientPhone, hasInsurance, clientBalanceOwed, clientLastDateVisited,
clientNextVisitDate)
values
('Darcy Watts','dwatts#email.com','m','1996-5-9','Belfast, Charlottetown
PEI','123-222-3333','y','400.77','2017-8-12','2019-9-6');
When I try and run this I am met with this error:
#1109 - Unknown table 'client' in field list
So from what I've learned over the last few hours is that this error usually happens when you put the '`' (backtick) on a variable or table name, MySQL thinks that entry is part of a field list or something along that line. So I changed the trigger to just be 'client' by itself and I still got an error. Dropped the old database and everything. One more thing, the insert statement does work by itself if the trigger has not been entered yet.
Any help would be appreciated! Thanks!
I guess your hasInsurance should be from the new record.
...
if(new.hasInsurance = 'y') then
insert into insurance (clientNum) values (NEW.clientNum);
end if;
...
--
DB Fiddle (provided by GMB)
I am working on setting up a database and have created tables that cascade out like using the cascade on update for the foreign key. I have two tables being worked with to try to solve this. My tables are like this:
create table Item(Item int(4) not null, EquipName varchar(20), Equip int(4)
not null, primary key(Item, Equip))
create table Cross(Time timestamp default now(), Cross varchar(10) default
'null', Item int(4) not null, Equip int(4) not null, Stop varchar(20) default
'null', foreign key(Item, Equip) references Item(Item, Equip) on update
cascade);
So I want to be able to enter data into the Item table and have it be automatically put into Cross table where those values are cascaded to. This is why Cross and StopCount have a default value of null.
For example insert into Item values(5, fan, 4); I would like Cross to be populated in the FK spots automatically to what information was entered into Item.
You can use a trigger to autofill another table.
DELIMITER $$
CREATE TRIGGER init_cross AFTER INSERT ON item
FOR EACH ROW
BEGIN
INSERT INTO `cross`(item,equip) VALUES( NEW.item, NEW.equip );
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'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)
I imagine that I have designed my database badly, but I'm currently stumped by the fact that I need to use dynamic sql in a trigger and that's making mysql unhappy.
The context is that I have created a membership database with several dozen tables, the main one of which is the 'member' table with a unique primary key 'id'. There are a number of other tables which have foreign keys referring to the member.id field.
Because the data has been gathered over many years and with little dupe-control, there is another field in the 'member' table called 'superseded_by', which contains the id of the member who supersedes this one. By default, superseded_by is set to be the member_id. Any one whose superseded_by <> id is deemed to be a dupe.
Now the tricky part... when we identify a dupe, we want to set the superseded_by field to point to the new primary member and update all the tables with foreign keys pointing to the now redundant member id. I have tried to do this using an after update trigger... and then I've tried to be clever by querying the foreign keys from the information_schema and using dynamic sql to update them.
This clearly doesn't work (Error Code: 1336 Dynamic SQL is not allowed in stored function or trigger).
I'm assuming there is a better way to design the schema / handle dupes which I haven't thought of.
Help please...
CODE SNIPPET:
-- ---
-- Table 'member'
-- ---
DROP TABLE IF EXISTS member;
CREATE TABLE member (
id INTEGER AUTO_INCREMENT,
superseded_by INTEGER DEFAULT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE DEFAULT NULL,
gender ENUM('M', 'F') DEFAULT NULL,
mailing_address_id INTEGER DEFAULT NULL,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (mailing_address_id) REFERENCES mailing_address (id),
FOREIGN KEY (superseded_by) REFERENCES member (id)
);
DELIMITER $$
CREATE TRIGGER set_superseded_by_on_insert BEFORE INSERT ON member FOR EACH ROW
BEGIN
SET NEW.superseded_by = NEW.id;
END$$
-- Trigger to update other tables (volunteers, donations, presenters, etc.) when member's superseded_by record is updated
-- Assumes the new superseding person exists (they should also not be superseded by anyone themselves)
CREATE TRIGGER adjust_foreign_member_keys_on_superseded_by_update AFTER UPDATE ON member FOR EACH ROW
BEGIN
DECLARE db, tbl, col VARCHAR(64);
DECLARE fk_update_statement VARCHAR(200);
DECLARE no_more_rows BOOLEAN;
DECLARE fks CURSOR FOR SELECT kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.COLUMN_NAME
FROM information_schema.TABLE_CONSTRAINTS tc
JOIN information_schema.KEY_COLUMN_USAGE kcu ON
tc.table_schema = kcu.table_schema AND tc.constraint_name = kcu.constraint_name
WHERE tc.constraint_type='FOREIGN KEY' AND
kcu.REFERENCED_TABLE_NAME = 'member' AND
kcu.REFERENCED_COLUMN_NAME = 'id';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
IF NEW.superseded_by <> OLD.superseded_by THEN
OPEN fks;
SET no_more_rows = FALSE;
update_loop: LOOP
FETCH fks INTO db, tbl, col;
IF no_more_rows THEN
LEAVE update_loop;
END IF;
SET #fk_update_statement = CONCAT("UPDATE ", db, ".", tbl, " SET ", col, " = NEW.superseded_by WHERE ", col, " = NEW.id;");
PREPARE stmt FROM #fk_update_statement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE fks;
END IF;
END$$
DELIMITER ;
Why are you trying to maintain duplicates in your main table? Seems like you'd be better off with a member table and a member_history table to track previous changes. You could do it by having a table that stored the field changed, date changed and the old and new values. Or you could just store the previous snapshot of the member table before updating it. For instance:
INSERT INTO member_history SELECT NULL, * FROM member WHERE id = ?
UPDATE member SET [...] WHERE id = ?
The schema for member_history would be nearly identical except that you would store member.id as member_id and have a separate primary key for each history entry. (Note: I'm glossing over the syntax a little, the NULL, * part might not work in which case you may need to explicitly name all the fields. Haven't taken the time to check it).
CREATE TABLE member (
id INTEGER AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE DEFAULT NULL,
gender ENUM('M', 'F') DEFAULT NULL,
mailing_address_id INTEGER DEFAULT NULL,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (mailing_address_id) REFERENCES mailing_address (id),
);
CREATE TABLE member_history (
id INTEGER AUTO_INCREMENT,
member_id INTEGER NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE DEFAULT NULL,
gender ENUM('M', 'F') DEFAULT NULL,
mailing_address_id INTEGER DEFAULT NULL,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (member_id) REFERENCES member (id),
);
Notice that I removed the superseded_by field in the member table and the foreign key to mailing_address in the member_history table. You shouldn't need the superseded_by any more and keeping the foreign key in the member_history table isn't really necessary unless you're worried about dangling references in your history.
Ok, just a couple of thoughts on this:
superseded_by is referencing id on the same table and is in general equal to the latter - not in those cases where you were able to identify a dupe, though, in which case it would point to another already existing member's id.
Given that we can safely assume that no superseded_by field will ever hurt the foreign key constraint.
I further assume that id and superseded_by fields of dupes that have not been identified yet are equal.
So, if all of the above is true, you may bend the foreign key of the other related tables to reference superseded_by instead of id. This way you could cascade the changes made to the dupe down to the other tables and still have the exact same constraint as before.
What you think? Am I missing something?
Please note that this is an option only if you are using InnoDB rather than MyISAM.
Regards,
aefxx
Trigger and stored function in mysql have limitations that we can not use dynamic sql in both of these. I hope this helps.