How can I correctly format my SQL trigger? - mysql

For some background this is part of an assignment for my Databases class. We have a set of tables that all entail towards Company information. This includes WORKS_ON which shows the hours that Employees work on specific projects. The goal of my code was to write a trigger that prevents a user from assigning more than 40 hours of total work to one employee. I intended to do this via obtaining the sum of hours that are currently in the table associated with the new row's Employee SSN(Essn). From there I meant to add the sum to the new row's hours and compare to 40. If the amount was more than 40, then a custom message is concatenated and an error is raised to the user. The professor included PHP which handles the printing of that error for me. As of now the only potential fix/error I can think of is that it is not legal to perform arithmetic within the if statement. Please let me know if you see anything I need to fix/improve.
DELIMITER &&
CREATE TRIGGER MAXTOTALHOURS
BEFORE INSERT ON WORKS_ON FOR EACH ROW
BEGIN
DECLARE sumHours integer;
DECLARE customMessage VARCHAR(100);
SELECT SUM(hours) INTO sumHours FROM WORKS_ON WHERE Essn = New.Essn;
IF (sumHours + New.hours > 40) THEN
SET customMessage = concat('You entered', New.hours, '. You currently work ', sumHours, '. You are over 40 hours!');
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = customMessage;
END IF;
END &&
DELIMITER ;

Related

Can't create MySQL Trigger with several tables

I've got a database with 3 tables:
delivery
company
details
The company table has a column with ratings from 1 to 10 and if a rating is more than 5 we can understand that this company is reliable and in detail if the price is more than 1000 it is expensive detail.
Table delivery is connecting table for company and details Now I'm trying to create a trigger that will block Insert when somebody trying to add in table delivery expensive detail with an unreliable company, but I can't understand how to create Trigger using data from different tables.
I'm using MySQL
DELIMITER //
CREATE TRIGGER before_insert_1
BEFORE INSERT
ON delivery
FOR EACH ROW
IF company.rating < 5 AND detail.Det_Price > 1000 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Unreliable company';
END IF //
DELIMITER ;
You should review https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html paying particular attention to the discussion of NEW. values.
A simple version of the trigger might be like
DELIMITER //
CREATE TRIGGER before_insert_1
BEFORE INSERT
ON delivery
FOR EACH ROW
begin
declare vrating int;
declare vprice int;
select company.rating into vrating from company where company.id = new.company_id;
select detail.det_price into vprice from detail where detail.? = new.?;
IF vrating < 5 AND vPrice > 1000 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Unreliable company';
END IF ;
end //
DELIMITER ;
but since you didn't publish table definitions I can't tell you exactly how the selects should be.

mysql: I didn't update the table defining the trigger in the trigger, but i got the similar error

I have three tables. They are called book, reader, borrow.
The function of my trigger: insert borrowing records into the borrow table. But before inserting, we have to check the status of the reader's library card and the status of the book. Only when they are all normal can we lend the book to the reader, that is, change the status of the borrowed book in the book table to "borrowed", and then insert the borrowing record into the borrow table. However, some errors occurred when updating the book table.
Please look at my note 1, which reported me such an error: [stored trigger is already used by statement which invoked stored trigger]. I know that the trigger of MySQL can't modify the defined table, so I think this error is very puzzling. Then I created a new account table and added the sentence marked in Note 2 to the trigger. It works properly. So this mistake really confused me.
I would be very grateful if you could help me. Followings is my code:
DROP TRIGGER IF EXISTS before_insert_borrow;
CREATE TRIGGER before_insert_borrow BEFORE INSERT ON borrow
FOR EACH ROW
BEGIN
DECLARE cst VARCHAR(10);
DECLARE b_cts VARCHAR(10);
SELECT Card_status FROM reader WHERE read_no = NEW.read_no INTO cst;
SELECT B_status FROM book WHERE book_no = NEW.book_no INTO b_cts;
IF cst = 'loss' THEN
SIGNAL SQLSTATE '09000' SET MESSAGE_TEXT = 'your card is lost……';
ELSEIF b_cts = 'checked' THEN
SIGNAL SQLSTATE '09000' SET MESSAGE_TEXT = 'the book you want has been checked out.';
END IF;
UPDATE book SET B_status = 'checked' WHERE book_no = NEW.book_no; -- 1
UPDATE account SET total = total + 1000 WHERE id =1; -- 2
END;
-- test data
INSERT INTO borrow(read_no,book_no)VALUES('001','B17682');

Why do I keep getting Error 1172 in this MySQL trigger?

What I'm trying to do is this:
Create a trigger named trg_cust_balance that will automatically update the AC_CUSTOMER table’s CUS_BALANCE before a new CHARTER row is added. Use the CHARTER table’s CHAR_TOT_CHG as the update source (Assume that all charter charges are charged to the customer balance.) In addition to the CHAR_TOT_CHG, add $25 for every quart of oil used on the charter.
This is my code:
create trigger trg_cust_balance
before insert on CHARTER
for each row
begin
declare CHG_PRIOR double;
declare OIL_QUANT double;
select CHAR_OIL_QTS into OIL_QUANT
from CHARTER
order by CUS_CODE;
select CHAR_TOT_CHG into CHG_PRIOR
from CHARTER
order by CUS_CODE;
update AC_CUSTOMER
set CUS_BALANCE = CUS_BALANCE + CHG_PRIOR + (OIL_QUANT * 25);
end;
// delimiter ;
So basically what I'm doing here is pulling the quantity of quarts of oil (CHAR_OIL_QTS) and the total charge (CHAR_TOT_CHG) and turning them into variables. Then trying to make the customer balance (CUS_BALANCE) equal to whatever it was plus said charges. However, I keep getting the 1172 error and I don't know why. I suspect it's got to do with the two select statements. Any help would be appreciated, thanks
And you can reduce it to following
But the update clause needs an other WHERE Clause so that the correct custimer gets the balanced changed
drop trigger if exists trg_cust_balance;
delimiter //
create trigger trg_cust_balance
before insert on CHARTER
for each row
begin
update AC_CUSTOMER
set CUS_BALANCE = CUS_BALANCE + NEW.CHAR_TOT_CHG + (NEW.CHAR_OIL_QTS * 25)
where AC_CUSTOMER.CUS_CODE = NEW.CUS_CODE;
end//
delimiter ;
INSERT INTO CHARTER VALUES(10019,'2020-02-10','4278Y','TYS',644,3.8,4.5,167.4,0,10017);
Thsi runs with out a proem seee https://www.db-fiddle.com/f/29eiHwfNHDrL34VQ8CSPAF/1

SQL PROCEDURE ISSUE,not sure on placement of data

Write a stored procedure that updates the weekly duty roster for an employee and allocates him/her a maximum of 5 work shifts in a given branch. To update the roster, the procedure ensures that:
i. The employee has an existing roster allocated to her/him. In case there is no existing roster for a given employee, the procedure doesn’t update the roster, rather prints an appropriate error message.
ii. The day of current roster is shifted by one day in the updated roster. For instance, if the current roster for an employee shows work shifts from Monday to Friday, then the updated roster will allocate work shifts from Tuesday to Saturday. For simplicity, we will assume that the type and number of work shifts allocated to an employee remain
same from week to week unless an exception occurs such as overallocation. However, the manager may wish to add any extra work shifts to an employee manually.
iii. A warning message is displayed in case the allocated hours of work for an employee exceeds the standard hours of work (35 hours per week).
any help is appreciated having a lot of trouble getting this right.. MYSQL
DELIMITER //
DROP PROCEDURE IF EXISTS updateRoster
CREATE PROCEDURE UpdateWeeklyRoster (IN e CHAR(8), IN b INT)
BEGIN
-- variable declaration
DECLARE WORKING_HOURS INT(35);
DECLARE Updated_Working_Shift_ID INT;
-- cursor declaration
DECLARE c1 CURSOR FOR
EmployeeID,BranchID,WorkingShiftID;
DECLARE CONTINUE HANDLER FOR NOT FOUND set finished = 1;
-- check whether the employee exists or not
SELECT EmployeeID into e
FROM DutyRoster
WHERE EmployeeID = e;
IF e IS NULL THEN
SIGNAL SQLSTATE '45000' set MESSAGE_TEXT = 'THERE IS EXISTING ROSTER';
ELSE
-- actual part
-- delete existing tuples relevant to the given employee and branch id from the Duty Roster table
OPEN c1;
-- execute a loop
REPEAT
FETCH c1 into .... , ....., .....;
-- find the workingShiftWeekDay: assign appropriate values into Current_Week_Day
-- Find the Updated_Week_Day
SET Updated_Week_Day =
CASE Current_Week_Day
WHEN Week_day='MONDAY' THEN Updated_Week_Day='TUESDAY';
WHEN 'TUESDAY' THEN 'WEDNESDAY';
WHEN 'THURSDAY' THEN 'FRIDAY';
WHEN 'FRIDAY' THEN 'SATURDAY';
WHEN 'SATURDAY' THEN 'SUNDAY';
WHEN 'SUNDAY' THEN 'MONDAY';
ELSE .....
END CASE;
-- Find the current_duty_type
-- for the updated_week_day, find an appropriate working shift id
SELECT dutyType into WorkingShiftID
FROM WorkingShift
WHERE EmployeeID AND dutyType = CurrentDutyType;
-- insert the new record
INSERT INTO DutyRoster VALUES (e,b,Updated_Working_Shift_ID);
UNTIL ........;
END REPEAT;
CLOSE c1;
-- Checking whether an employee works for more than 35 hours
SELECT HOUR(TIMEDIFF(WorkingShiftEndTime,WorkingShiftStartTime)) INTO TOTAL, WORKING_HOURS
from WorkingShift, DutyRoster
WHERE DutyRoster, WorkingShiftID = WorkingShift, WorkingShiftID
AND EmployeeID = e;
IF TOTAL, WORKING HOURS > 35 THEN
SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = 'Employee is working over 35 hours....';
END IF;
END IF;
END
DELIMITER ;
DELIMITER //

MySQL Stored Procedures with detail table and transactions

I have a relatively large (for me) stored procedure that I'm working on that is responsible for maintaining inventory values whenever a "movement" occurs.
I originally had all the checks and inserts happening in PHP, but I felt that the number of things that need to happen in the database made it more controllable by putting it in a stored procedure and denying INSERT/UPDATE permissions to these tables.
So history aside, I'm curious if there is a way to run a prepared statement for the stored procedure in which I can pass multiple values that will be insert into a detail table.
For example, let's say I want to insert the following data into a parent and children records.
"ACME", "TNT", 100
"ACME", "Anvil", 5
The parent record would contain ACME, and the children records would have the details of TNT, 100 and Anvil, 5.
Is it possible to somehow pass this data into a single stored procedure?
The reason I'm hoping to do this in one pass, is let's say we don't have enough TNT available, I don't want the original parent record to be inserted unless the entire transaction can take place.
Would concatenating the data sent to the stored procedure work? I don't imagine sending more than 1000 detail lines at a time to the database.
do_movement('ACME','2:TNT,100||Anvil,5'); The 2 in front denotes the number of details to expect, might help with error catching. Just a thought.
This way the code would be responsible for formatting, but I could output SQLEXCEPTIONS and have everything happen in one true transaction.
Drawing heavily from this post: MySQL Split Comma Separated String Into Temp Table
Using the SPLIT_STR function from (also referenced in previous post): http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
And then my functionality to parse N:s1|s2|..|sN and check.
I could easily call SPLIT_STR again if I had something like N:s1,a1|s2,a2|..|sN,aN
BEGIN
DECLARE defined_quantity INT;
IF serial_numbers REGEXP '^[^0]{0,}[[:digit:]]+:.*$' = TRUE THEN -- check that we are indeed at least starting properly formatted.
SET defined_quantity = CONVERT(LEFT(serial_numbers,LOCATE(':',serial_numbers)-1), UNSIGNED);
IF defined_quantity <= 0 THEN
SIGNAL SQLSTATE '45006'
SET MESSAGE_TEXT = 'The quantity defined with the serial number list is <= 0.';
END IF;
SET serial_numbers = RIGHT(serial_numbers,LENGTH(serial_numbers) - LOCATE(':',#serial_numbers));
BEGIN
DECLARE a INT Default 0 ;
DECLARE str VARCHAR(255);
DECLARE q INT;
simple_loop: LOOP
SET a=a+1;
SET str=TRIM(SPLIT_STR(serial_numbers,"|",a));
IF str='' THEN
SET a=a-1; -- we ignore the last increment
LEAVE simple_loop;
END IF;
#Do Inserts into temp table here with str going into the row
INSERT INTO transaction_detail (transaction_id, serial_number,created_at,updated_at) VALUES(transaction_id, str,NOW(),NOW());
END LOOP simple_loop;
SELECT a, defined_quantity, quantity;
IF a <> defined_quantity OR a <> quantity OR defined_quantity <> quantity THEN
SIGNAL SQLSTATE '45007'
SET MESSAGE_TEXT = 'The quantities do not match for the serial numbers provided.';
END IF;
END;
ELSE
SIGNAL SQLSTATE '45005'
SET MESSAGE_TEXT = 'The serial number formatted list is not properly formatted. Please provide in "n:s1|s2|s3|...|sn" format.';
END IF;
END;