SQL PROCEDURE ISSUE,not sure on placement of data - mysql

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 //

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.

How can I correctly format my SQL trigger?

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 ;

Why stored procedure takes more time to execute compared to stored function for same code?

I have created stored-procedure with OUT parameter to handle automate INSERT operations based on values of other tables. I also made stored-function for the same operation. I am facing a problem with execution time for both is heavily different which makes me worried and I don't know why is that.
Following table values in summary:
2016 rows in emp_personal
I am using MariaDB 10.3.14. I am testing with HeidiSQL 10.1.0.5577.
I also tried calling it from Node.JS using MariaDB Node.js Connector. But in both cases, I had almost the same result.
This is Stored Procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_auto_attendance`(
OUT `result` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE empId,done,isHoliday,isEmpHoliday,isWeekOff,isApprovedLeave,insertedRows INT DEFAULT 0;
DECLARE attendance VARCHAR(20) DEFAULT 'Present';
DECLARE empIds VARCHAR(500) DEFAULT '';
-- declare cursor with all employee Ids who are active
DECLARE empIds_cursor CURSOR FOR
SELECT id from emp_personal where isActive=1;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- Check if today is holiday
SELECT COUNT(id) INTO isHoliday
FROM apexa_portal.public_holiday_dates
WHERE dateOfHoliday= CURDATE();
OPEN empIds_cursor;
get_id: LOOP
SET attendance = 'Present';
FETCH empIds_cursor INTO empId;
-- Following check if NOT FOUND handler has called, if yes exit loop else continue. NOTE: If you don't write this, it will be infinite loop.
IF done = 1 THEN
LEAVE get_id;
END IF;
-- Check if public holiday for employee
IF isHoliday > 0 THEN
SELECT COUNT(phtd.isActive) INTO isEmpHoliday FROM public_holiday_template_days as phtd
LEFT JOIN emp_office_details as eod ON eod.publicHolidayTemplate=phtd.templateId
LEFT JOIN public_holiday_dates as phd ON phd.pub_holiday_id=phtd.holidayId
WHERE eod.empID=empId AND phd.dateOfHoliday=CURDATE() AND phtd.isActive=1;
IF isEmpHoliday > 0 THEN
SET attendance = 'Public Holiday';
END IF;
END IF;
-- if not public holiday, Check if week off
IF attendance <> 'Public Holiday' THEN
SELECT COUNT(eowd.id) INTO isWeekOff FROM emp_office_working_days as eowd
WHERE DAY=DAYOFWEEK(CURDATE()) AND eowd.empId=empId AND eowd.isActive=0;
IF isWeekOff > 0 THEN
SET attendance = 'Week Off';
END IF;
END IF;
-- if not week off, Check if approved leave
IF attendance <> 'Week Off' THEN
SELECT COUNT(la.id) INTO isApprovedLeave FROM leave_application as la
WHERE la.fromDate <= CURDATE() AND la.toDate >= CURDATE() AND la.empID=empId AND la.leaveStatus='Approved';
IF isApprovedLeave > 0 THEN
SET attendance = 'Approved Leave';
END IF;
END IF;
-- insert attendance
INSERT INTO attendance_master(empID,dateOfAttendance,attendance) VALUES(empId,CURDATE(),attendance);
SET insertedRows = insertedRows + 1;
END LOOP get_id;
CLOSE empIds_cursor;
SET result = insertedRows;
END
This is Stored Function
CREATE DEFINER=`root`#`localhost` FUNCTION `test_auto_att_func`()
RETURNS int(11)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE empId,done,isHoliday,isEmpHoliday,isWeekOff,isApprovedLeave,insertedRows INT DEFAULT 0;
DECLARE attendance VARCHAR(20) DEFAULT 'Present';
DECLARE empIds VARCHAR(500) DEFAULT '';
-- declare cursor with all employee Ids who are active
DECLARE empIds_cursor CURSOR FOR
SELECT id from emp_personal where isActive=1;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- Check if today is holiday
SELECT COUNT(id) INTO isHoliday
FROM apexa_portal.public_holiday_dates
WHERE dateOfHoliday= CURDATE();
OPEN empIds_cursor;
get_id: LOOP
SET attendance = 'Present';
FETCH empIds_cursor INTO empId;
-- Following check if NOT FOUND handler has called, if yes exit loop else continue. NOTE: If you don't write this, it will be infinite loop.
IF done = 1 THEN
LEAVE get_id;
END IF;
-- Check if public holiday for employee
IF isHoliday > 0 THEN
SELECT COUNT(phtd.isActive) INTO isEmpHoliday FROM public_holiday_template_days as phtd
LEFT JOIN emp_office_details as eod ON eod.publicHolidayTemplate=phtd.templateId
LEFT JOIN public_holiday_dates as phd ON phd.pub_holiday_id=phtd.holidayId
WHERE eod.empID=empId AND phd.dateOfHoliday=CURDATE() AND phtd.isActive=1;
IF isEmpHoliday > 0 THEN
SET attendance = 'Public Holiday';
END IF;
END IF;
-- if not public holiday, Check if week off
IF attendance <> 'Public Holiday' THEN
SELECT COUNT(eowd.id) INTO isWeekOff FROM emp_office_working_days as eowd
WHERE DAY=DAYOFWEEK(CURDATE()) AND eowd.empId=empId AND eowd.isActive=0;
IF isWeekOff > 0 THEN
SET attendance = 'Week Off';
END IF;
END IF;
-- if not week off, Check if approved leave
IF attendance <> 'Week Off' THEN
SELECT COUNT(la.id) INTO isApprovedLeave FROM leave_application as la
WHERE la.fromDate <= CURDATE() AND la.toDate >= CURDATE() AND la.empID=empId AND la.leaveStatus='Approved';
IF isApprovedLeave > 0 THEN
SET attendance = 'Approved Leave';
END IF;
END IF;
-- insert attendance
INSERT INTO attendance_master(empID,dateOfAttendance,attendance) VALUES(empId,CURDATE(),attendance);
SET insertedRows = insertedRows + 1;
END LOOP get_id;
CLOSE empIds_cursor;
RETURN insertedRows;
END
Time to execute Stored Function
SELECT `test_auto_att_func`();
/* Affected rows: 0 Found rows: 1 Warnings: 0 Duration for 1 query: 0.328 sec. */
Time to execute Stored Procedure
CALL `sp_auto_attendance`(#res);
SELECT #res;
/* Affected rows: 6,049 Found rows: 1 Warnings: 0 Duration for 2 queries: 00:01:21.6 */
Stored Function Output and Stored Procedure Output
Can anyone please explain why this is happening? And if I am doing anything wrong, please let me know how should I correct it?
Thank You.
There should be no difference on performance on similar code when you run the code as a procedure vs as a function.
Most likely explanation (see Affected rows) is that at the the time of the function run, the cursor did not find any active persons whereas in the procedure run did, hence running the loop queries.

How to fix Syntax Error in DATEDIFF trigger

Can anyone see what is wrong with this code?
Everything was working fine until I added the trigger to calculate the difference between two dates. First of all I altered the table just incase anyone wondered.
ALTER TABLE bookings
ADD COLUMN TheDuration varchar(10);
Then my triggers are as follows...
DELIMITER //
CREATE TRIGGER check_licence /*This trigger will approve customers with a valid licence */
BEFORE UPDATE ON customers
FOR EACH ROW
BEGIN
SET NEW.Status = CASE WHEN NEW.valid_licence = 'Yes'
THEN 'Approved'
ELSE 'Unapproved' /*So if a Customer has a valid licence, He will be automatically approved. */
/*But if he doesn't he will become unapproved[WORKING]*/
SET NEW.TheDuration = DATEDIFF(NEW.bookings.end_date, NEW.bookings.start_date) -- -- TO CALCULATE DURATION BETWEEN 2 DATES
END;
//
DELIMITER ;
The problem lays within
SET NEW.TheDuration = DATEDIFF(NEW.bookings.end_date, NEW.bookings.start_date) -- -- TO CALCULATE DURATION BETWEEN 2 DATES
As everything was working before I added this.
Same here...
DELIMITER //
CREATE TRIGGER Carperperson /* This Trigger Blocks a customer from renting two cars on the same name twice on one day. */
BEFORE INSERT ON bookings /*E.g. Mr.ABC cannot rent a Ford and a Nissan on the same day. Has to return first car first.[WORKING]*/
FOR EACH ROW
BEGIN
IF EXISTS (
SELECT 1
FROM bookings
WHERE NEW.customer_id = bookings.customer_id
AND ((new.start_date >= bookings.start_date
and new.start_date < bookings.end_date)
or (new.end_date > bookings.start_date
and new.end_date < bookings.end_date))
) THEN
SIGNAL SQLSTATE '45000'
set message_text='You can only book one car per single customer a day!' ; /* This triggers only allows to rent a car for 7 days, not more, not less[WORKING]*/
END IF;
IF ( NEW.end_date > NEW.start_date + INTERVAL 7 DAY ) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '7 is the maximum. Please choose an earlier date.'; /*The end_date is more than seven days after start_date*/
END IF;
SET NEW.TheDuration = DATEDIFF(NEW.bookings.end_date, NEW.bookings.start_date) -- -- TO CALCULATE DURATION BETWEEN 2 DATES
END;
//
DELIMITER ;
Problem lays within...
SET NEW.TheDuration = DATEDIFF(NEW.bookings.end_date, NEW.bookings.start_date) -- -- TO CALCULATE DURATION BETWEEN 2 DATES

MySQL Stored Procedure Updating Status of Accounts Error Code: 1329

I have a situation in which I would like to iterate through a "schedPayments" table that stores a schedule of payments corresponding with a client in the "client" table. The client table also contains a "status" column at the moment holds a 0 for "Past Due" and a 1 for "Current". When the balance from the client table is greater than the supposed balance from the schedPayments table AND today's date is later than the date the payment was scheduled for, the status column in the clients table should be set to 0.
I may be completely off the wall with my solution, but I keep getting Error Code: 1329. No Data - zero rows fetched, selected or processed. The MySQL Workbench lacks some major debugging capabilities that I wish it had. The documentation also doesn't quite cover what I need either in this situation.
CREATE PROCEDURE `project`.`status_update` ()
BEGIN
DECLARE balance DECIMAL(20) DEFAULT 0;
DECLARE cID INT(10) DEFAULT 0;
DECLARE currentID INT(10) DEFAULT 0;
DECLARE supposedBal DECIMAL(20) DEFAULT 0;
DECLARE payDate DATE;
DECLARE cur1 CURSOR FOR SELECT ClientID,SupposedBalance,Date FROM project.schedpayments;
OPEN cur1;
status_loop: LOOP
FETCH cur1 INTO cID, supposedBal, payDate;
BLOCK2: BEGIN
DECLARE cur2 CURSOR FOR SELECT balance FROM project.client WHERE ID=cID;
OPEN cur2;
FETCH cur2 INTO balance;
IF currentID > cID THEN
SET currentID = cID;
IF (CURDATE() > payDate) AND (supposedBal < balance) THEN
UPDATE feeagree SET Status=0 WHERE ID=cID;
END IF;
CLOSE Cur2;
END IF;
END BLOCK2;
END LOOP;
CLOSE cur1;
END $$
You can see the remnants of how I had enclosed the entire procedure in a block and that only resulted in the compiler thinking the first block ended with END BLOCK2; and that resulted in an Error Code 1325. Cursor is already open.
I am definitely making this more complicated than necessary, so any help would be much appreciated. The only way I learn this stuff is trial by fire, and it is super hot today.
It seems that you don't need all those cursors and you can achieve your goal with one UPDATE statement.
It's hard to be precise without seeing your tables structures and sample data, but a more succinct version of your SP might look like this
CREATE PROCEDURE status_update()
UPDATE feeagree
SET Status = 0
WHERE ID IN
(
SELECT p.cID
FROM schedpayments p JOIN client c
ON p.cID = p.ID
WHERE p.Date < CURDATE()
AND p.SupposedBalance < c.balance
GROUP BY p.cID
);
...
DECLARE done TINYINT DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = true;
OPEN cur1;
status_loop: LOOP
FETCH cur1 INTO cID, supposedBal, payDate;
IF DONE = true THEN LEAVE status_loop; END IF;
...
SET DONE = false;
END LOOP;
The SET DONE = false at the end resets DONE in case anything in the inner block results in it getting set to TRUE;