mySql trigger not working as intended - mysql

I have 2 tables set up with a trigger, such that an insertion upon one table, will trigger an increment in the column of the second table at the same primary key (as both tables a linked by a foreign key), however at the moment the increment only once, then it does not increment for any subsequent insertions. My thinking is that it's probably with the way I set up the keys but I'm unsure, could someone please shed some light on this:
CREATE TABLE Members(
ID INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
FIRST_NAME TEXT(16),
LAST_NAME TEXT(16),
TITLE TEXT(7), /** 7 CHARS for 'Student'*/
INSTITUTION VARCHAR(2048),
No_Publications INT NOT NULL,
PRIMARY KEY(ID)
);
CREATE TABLE Papers(
ISBN INT UNSIGNED NOT NULL UNIQUE,
Title TEXT(4),
Publish_Date DATE NOT NULL,
Topic TEXT(128),
PRIMARY KEY(ISBN)
);
CREATE TABLE Publications(
Author_ID INT UNSIGNED NOT NULL UNIQUE,
ISBN INT UNSIGNED NOT NULL UNIQUE,
PRIMARY KEY (Author_ID, ISBN),
FOREIGN KEY(Author_ID) REFERENCES Members(ID) ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY(ISBN) REFERENCES Papers(ISBN) ON DELETE RESTRICT ON UPDATE CASCADE /**Used in
many to many relations*/
);
CREATE TRIGGER New_Publication AFTER INSERT ON Publications
FOR EACH ROW
UPDATE Members SET No_Publications = No_Publications + 1
WHERE Members.ID = Publications.Author_ID;
EDIT: I want the No_Publications to increase with insertion upon the publications table
EDIT 2: After implementing the NEW keyword the auto-incrementation now works however now the following occurs:
Upon adding 1 new paper to 1 new member, the paper is registered in the database, however, subsequent additions of papers to the same member are not registered, despite and increment occuring in the members table.
Insertion into the Publications table occurs as a result of the papers table which has been appended above for transparency.

The trigger should be:
CREATE TRIGGER New_Publication AFTER INSERT ON Publications
FOR EACH ROW
UPDATE Members SET No_Publications = No_Publications + 1
WHERE Members.ID = NEW.Author_ID;
Even though his column should be updated also on deletes and having this column goes agains database normalization as you can get this information without having the column so it can be only useful in certain cases to improve performance and avoid counts.

Related

MySQL auto assign foreign key ID

I have a main table called results. E.g.
CREATE TABLE results (
r_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
r_date DATE NOT NULL,
system_id INT NOT NULL,
FOREIGN KEY (system_id) REFERENCES systems(s_id) ON UPDATE CASCADE ON DELETE CASCADE
);
The systems table as:
CREATE TABLE systems (
s_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
system_name VARCHAR(50) NOT NULL UNIQUE
);
I'm writing a program in Python with MySQL connector. Is there a way to add data to the systems table and then auto assign the generated s_id to the results table?
I know I could INSERT into systems, then do another call to that table to see what the ID is for the s_name, to add to the results table but I thought there might be quirk in SQL that I'm not aware of to make life easier with less calls to the DB?
You could do what you describe in a trigger like this:
CREATE TRIGGER t AFTER INSERT ON systems
FOR EACH ROW
INSERT INTO results SET r_date = NOW(), system_id = NEW.s_id;
This is possible only because the columns of your results table are easy to fill in from the data the trigger has access to. The auto-increment fills itself in, and no additional columns need to be filled in. If you had more columns in the results table, this would be harder.
You should read more about triggers:
https://dev.mysql.com/doc/refman/8.0/en/create-trigger.html
https://dev.mysql.com/doc/refman/8.0/en/triggers.html

MySQL if exists update else create new

I have a vet table and a medical table with a 1 to many relationship, and the ID's are auto incremented.
CREATE TABLE vet(
vetID INT NOT NULL AUTO_INCREMENT,
vetPractice varchar(35),
Address varchar(150),
contactNumber varchar (15),
PRIMARY KEY (VetID)
);
CREATE TABLE medical(
medicalID INT NOT NULL AUTO_INCREMENT,
medication VARCHAR (200),
PRIMARY KEY (medicalID),
FOREIGN KEY (vetID) REFERENCES vet(vetID)
);
Users can enter details of a vet, i want a query to determine;
if the the vet details entered already exist, then update the foreign key in vetID(medical) with the entered vetID.
else if the vet does not exist create a new vet and update the foreign key in vetID(medical) with the newly created vetID.
You want to do an upsert in MySql. I think that this link can be helpful.
Example:
INSERT INTO table (x,x,x) VALUES (x,x,x)
ON DUPLICATE KEY UPDATE x=x+x;
You have to do a "select" query to determine whether exists .
Select medicalID from medical where medication=?
This should be executed in a different query as the update , you need to use server language to see if the results brings back a result.Then using an "if" statement determine your routine.
hope this helps.

Updating same table in stored procedure after UPDATE and INSERT

RELATIONSHIP : students (1 can have N) addresses
SCENARIO: Students can have many records but only one associated record must have 'current' field set as 'Yes' (other value is NULL) so the query below should always return only one record per student.
SELECT * FROM address WHERE student_id = 5 AND current = 'Yes'
PROBLEM:
People sometimes mark more than one record as 'Yes' after INSERT or UPDATE for same student so I need to avoid it. What is the best way of doing it by using triggers or stored procedures within MySQL?
If UPDATE happens on 'address' table then this should run somewhere to mark other records as NULL: UPDATE addresses SET current = NULL WHERE student_id = IN_student_id
If INSERT happens on 'address' table then this should run somewhere to mark other records as NULL: UPDATE addresses SET current = NULL WHERE student_id = IN_student_id AND id <> IN_inserted_id
Thanks in advance
If you need something updated automatically after data is modified, the right approach is a trigger. Notice a trigger may call a stored procedure.
However you will not be able to implement the described behaviour in a trigger because:
A stored function or trigger cannot modify a table that is already being used (for reading or writing) by the statement that invoked the function or trigger.
In fact, the information "Address X is the current address" should be stored in a column in the students table, as a foreign key to the address table. Therefore, unicity is guaranteed.
Something like this (fiddle with it here):
CREATE TABLE student (
id INT NOT NULL PRIMARY KEY,
current_address INT,
name VARCHAR(20)
);
CREATE TABLE address (
id INT NOT NULL PRIMARY KEY,
student_id INT NOT NULL,
contents VARCHAR(50) NOT NULL,
CONSTRAINT student_fk FOREIGN KEY student_fk_idx (student_id)
REFERENCES student(id)
);
ALTER TABLE student
ADD CONSTRAINT curraddr_fk_idx
FOREIGN KEY curraddr_fk_idx (id, current_address)
REFERENCES address(student_id, id);
Notice this structure allows insertion of students with no "current address". This is because at least one for the two tables must allow a NULL value for their foreign key (or else we cannot add a single row in either table). If it makes more sense, let address.student_id be NULL instead, and allow an address to be nobody's address until you create the corresponding student.

Foreign key as primary key in mysql

I have two dependent tables as
CREATE TABLE posts
(
post_id int(11) unsigned NOT NULL AUTO_INCREMENT,
title varchar(255),
PRIMARY KEY(post_id)
) ENGINE=InnoDB
CREATE TABLE post_meta
(
post_id int(11) unsigned REFERENCES posts(post_id) ON DELETE CASCADE,
info varchar(255),
PRIMARY KEY(post_id)
) ENGINE=InnoDB
Question 1: After INSERTing into posts, post_meta does not accept value with error Duplicate entry XX for key 'PRIMARY. How should I modify the table structure?
Question 2: How can I set to create a corresponding row in post_meta upon INSERT INTO posts? I mean creating an empty row (only having id of the FK) in post_meta when creating a row in posts. In other words having the same number of rows in two columns without any INSERT into the second column.
Your current implementation looks as out of normalized form. Are you sure you need to keep the data separated to two different tables? Maybe
CREATE TABLE posts
(
post_id int(11) unsigned NOT NULL AUTO_INCREMENT,
title varchar(255),
info varchar(255),
PRIMARY KEY(post_id)
) ENGINE=InnoDB
will do?
Speculating: if you're doing it that way because of security issues then MySQL supports Column-level privileges.
If normalizing your data is unacceptable for some reasons then you can just make post_id primary key in both tables (don't make it foreign key!) and add INSERT and DELETE triggers on table posts which will insert or delete corresponding rows from post_meta.
UPDATE
You said in comment that you can have multiple records in post_meta table corresponding to single record in posts. That changes a lot:
You MUST NOT use primary key on post_id in table post_meta. Primary key MUST be unique in table scope.
You can set up your foreign key (the one you already tried to) - this will ensure that metadata is deleted automatically with posts.
If you do need PRIMARY KEY defined on post_meta then you shoud add a new (possibly auto_increment field) to post_meta table and use it as primary key. (Also, table can exist even without primary key but it's against most DB guidelines)
If you need to create a meta record(s) automatically for each post then you can add an INSERT trigger on posts as I've already suggested. Another approach is using a stored procedure (and only stored procedure) for adding records to posts - and in this SP you can write some SQL to insert necessary records to post_meta.

Design database table to check id in another table before allowing insert

I've got what I'm sure is a really simple database question, but I don't even know what to google, so hopefully someone can help me here.
I'm trying to create 2 tables that are interconnected. For example, lets say I have an employee table with employee name and employee id, then I'm creating another table, employee_bonus, with employee id and bonus amount. What I want is for the employee_bonus table to automatically check with the employee table every time a row is inserted to make sure that the employee id exists in the employee table and reject the insert if not. The thing is, there could be multiple bonuses for a single employee, so I just want it to check that it exists, not make it one to one. Does that make sense? Is that possible? How would I do that and WHAT is that called?
Also, I'm doing this in MySql.
From wikipedia: Foreign Key
In the context of relational databases, a foreign key is a referential constraint between two tables.
http://en.wikipedia.org/wiki/Foreign_key
The following is a basic example of what you are looking for:
CREATE TABLE employees (
employee_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
family_name VARCHAR(100) NOT NULL,
first_name VARCHAR(100) NOT NULL,
birth_date DATETIME NULL DEFAULT NULL,
PRIMARY KEY(employee_id)
) ENGINE=INNODB;
CREATE TABLE employee_bonus (
bonus_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
employee_id INT UNSIGNED NOT NULL,
bonus_value DECIMAL(10,2) DEFAULT 0.00,
PRIMARY KEY(bonus_id),
KEY fk_employee_id (employee_id),
CONSTRAINT fk_employee_constraint FOREIGN KEY fk_employee_id(employee_id)
REFERENCES employees(employee_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB;
Since it's not a 1-to-1 relationship give the employee_bonus table its own primary key and establish a foreign key relationship between the employee table's id column and the employee_id in the employee_bonus table.
If you want to avoid an error on insertion when attempting to add a missing employee, try this:
insert into employee_bonus
select ?, ?, ? -- put your insert values in here
from employee
where employee_id = ?; -- put the employee_id in here
This will insert one row if the employee exists and will do nothing if it doesn't.