Calculated Column in MySQL from Array / Formula - mysql

I'm very new to MySQL database. In Excel I have a formula that looks at the date cell and then states from that what Season the row of data is from.
In Excel I use this formula which looks at the month and day to determine which if the values from the array it falls into:
=LOOKUP(TEXT([DATE_CELL],"mmdd"),{"0101","0321","0621","0922","1221";"Winter","Spring","Summer","Autumn","Winter"})
Is there any way to setup a view or tag this onto a table in MySQL in order to update the season by itself? Many thanks for taking a look.

I would create a function like this:
CREATE DEFINER = 'root'#'localhost' FUNCTION `getSeason`(P_date DATE)
RETURNS varchar(10) CHARSET latin1
DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE v_season VARCHAR(10);
DECLARE v_month integer;
DECLARE v_day integer;
select date_format(P_date,'%m'),date_format(P_date,'%d') into v_month,v_day;
if (v_month<3 or (v_month=3 and v_day<21)) then
set v_season="Winter";
else if (v_month<6 or (v_month=6 and v_day>=21)) then
set v_season="Spring";
else if (v_month<9 or (v_month=9 and v_day>=21)) then
set v_season="Summer";
else if (v_month<12 or (v_month=12 and v_day<=21)) then
set v_season="Autumn";
else
set v_season="Winter";
end if;
end if;
end if;
end if;
RETURN v_season;
END;
And use the function in your WHERE clause: where getSeason(myDaeField)='Winter'
Or in your select :select getSeason(myDateField) as Season

Related

Stored Procedure - create a cursor - accept an OUT parameter

I was hoping to get some help the the below question, unfortunately the script I have created isn't working. Any assistance would be greatly appreciated!
Question:
Write a script that creates a stored procedure named test. This stored procedure should create a cursor for a result set that consists of the product_name and list_price columns for each product with a list price that’s greater than $700. The rows in this result set should be sorted in descending sequence by list price. The stored procedure should accept an OUT parameter where a message is passed out of the procedure. Then, the procedure should set the out parameter to a string variable that includes the product_name and list price for each product so it looks something like this:
Gibson SG,2517.00|Gibson Les Paul,1199.00|
Here, each value is enclosed in asterisk(*), each column is separated by a comma (,) and each row is separated by a pipe character (|).
My script:
CREATE PROCEDURE test( OUT message VARCHAR(200) )
BEGIN
DECLARE product_name_var VARCHAR(50);
DECLARE list_price_var DECIMAL(9,2);
DECLARE row_not_found TINYINT DEFAULT FALSE;
DECLARE s_var VARCHAR(400) DEFAULT '';
DECLARE invoice_cursor CURSOR for
SELECT
product_name,
list_price
FROM
products
WHERE
list_price > 700
ORDER BY list_price DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET row_not_found = TRUE;
OPEN invoice_cursor;
FETCH invoice_cursor INTO product_name_var, list_price_var;
WHILE row_not_found = FALSE DO
SET s_var = CONCAT(s_var,'*', product_name_var,'*,*',list_price_var,'*|');
FETCH invoice_cursor INTO product_name_var, list_price_var;
END WHILE;
SELECT s_var AS message;
END
SELECT s_var AS message; - message is seen as an alias
use
SET MESSAGE = s_var ;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=c43887fd68a17e5b1dc2093d10cd03ec
and beware nulls...

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.

error in stored procedure ...into keyword

Two tables Borrower(rollno,name,bookissue_date) and Fine(rollno,name,amount)
delimiter //
create procedure student( in roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
Declare cur cursor for
select Dateofiss from Borrower where Roll_no = roll into Dateofiss1;
OPEN cur;
fetch cur into Dateofiss1
if(datediff(sysdate(),Dateofiss1)<15) then varchar(20))
update Borrower set status='R'where Roll_no=roll_no
elseif(datediff(sysdate(),Dateofiss1)>=15)and datediff (sysdate(),Dateofiss1<30)
SET FINEAMOUNT=5*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
elseif (datediff(sysdate(),Dateofiss1)>30)
SET FINEAMOUNT=50*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
close cur;
end if
select * from Borrower;
elect * from Fine;
end
You have a number of syntax errors.
You have an extraneous varchar(20)) in the first if statement.
You're missing THEN in the ELSEIF statements.
You wrote update.borrower instead of update borrower.
You have roll_no in quotes in some of your update statements.
The roll_no parameter is the same as a table column, since column names are case-insensitive. The condition where Roll_no = roll_no will match every row because of this. Give the parameter a different name.
In a SELECT, the INTO clause goes after FROM, not at the end.
There's no need to use a cursor if you're using SELECT INTO. Just execute the query and it will set the variable.
You can also simplify the code by putting the date difference in a variable, so you don't have to repeatedly calculate it. And in the ELSEIF you don't need to test >= 15, since you'll only get there if the < 15 test failed.
The UPDATE statement is the same in all conditions, so it doesn't need to be in the IF at all.
delimiter //
create procedure student( in p_roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
declare diff INT;
select Dateofiss from Borrower into Dateofiss1 where Roll_no = p_roll_no;
OPEN cur;
SET diff = datediff(sysdate(),Dateofiss1)
IF diff BETWEEN 15 AND 29 THEN
SET FINEAMOUNT= 5 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
else
SET FINEAMOUNT= 50 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
end if
update Borrower set status='R'where Roll_no=p_roll_no
select * from Borrower;
select * from Fine;
end

How to check difference in each on OLD.* to NEW.* column in a MySQL Trigger?

Since SQL does not have a FOR-EACH statement, how could we verify if there is a difference on each value from the OLD object to the NEW object in a AFTER UPDATE type TRIGGER without knowing the table columns [and table names]?
Example today:
CREATE TRIGGER `audit_events_ugly`
AFTER UPDATE ON `accounts`
FOR EACH ROW
BEGIN
DECLARE changes VARCHAR(8000);
IF OLD.user_name <> NEW.user_name THEN
SET changes = 'user_name from % to %';
END IF;
IF OLD.user_type <> NEW.user_type THEN
SET changes = CONCAT(changes, ', user_type from % to %');
END IF;
IF OLD.user_email <> NEW.user_email THEN
SET changes = CONCAT(changes, ', user_email from % to %');
END IF;
CALL reg_event(how_canI_get_tableName?, #user_id, changes);
-- and that can go on and on... differently for every table.
END;
Example as I wish it could be:
CREATE TRIGGER `audit_events_nice`
AFTER UPDATE ON `accounts`
FOR EACH ROW
BEGIN
DECLARE changes VARCHAR(8000);
DECLARE N INT DEFAULT 1;
FOREACH OLD, NEW as OldValue, NewValue
BEGIN
IF OldValue <> NewValue THEN
SET changes = CONCAT(changes, ', column N: % to %');
SET N = N + 1;
END IF;
CALL reg_event(how_canI_get_tableName?, #user_id, changes);
-- now I can paste this code in every table that is audited..
END;
Any Ideas? WHILE, FOREACH, ARRAYS...
I think you cannot do that directly in a for-loop at the trigger level.
However, you could use a script to generate the trigger code. You would need to re-generate it every time you add/remove a field to the table (usually not frequently).

Error code 1292 Mysql DateTime

I am trying to format a date and a time that comes in one column called DATE as DD/MM/YYYY (Varchar) and in another column called TIME as HH:MM:SS into one variable to insert into another column (in Datetime data type). The code below is my procedure.
DROP PROCEDURE IF EXISTS TESTProc;
DELIMITER //
CREATE PROCEDURE TESTproc()
BEGIN
DECLARE LYEAR VARCHAR(45);
DECLARE LMONTH VARCHAR(45);
DECLARE LDAY VARCHAR(45);
DECLARE LTIME VARCHAR(45);
DECLARE LDATETIME DATETIME;
SELECT TIME FROM db.test_table INTO LTIME;
SELECT SUBSTRING(DATE,6,4) FROM db.test_table INTO LYEAR;
SELECT SUBSTRING(DATE,3,2) FROM db.test_table INTO LMONTH;
SELECT SUBSTRING(DATE,1,1) FROM db.test_table INTO LDAY;
SELECT CONCAT(LYEAR,'-', LMONTH,'-','0',LDAY,' ',LTIME) INTO LDATETIME;
INSERT INTO db.test_table(VC19) VALUES (LDATETIME);
END //
Call TESTProc;
When I run the procedure, I get an error code back:
Call TESTProc; Error Code: 1292. Incorrect datetime value: '2013-31-01 16:00:40' for column 'LDATETIME' at row 2
I only have one row in db.test_table. I do not have a column in the table called 'LDATETIME', this is just my local variable. I can see from the error that my format is correct for the DateTime 'YYYY-MM-DD HH:MM:SS'.
why I am getting this error?
Update: Here is how my code looks now:
DROP PROCEDURE IF EXISTS DateProc;
DELIMITER //
CREATE PROCEDURE Dateproc()
BEGIN
DECLARE LTIME VARCHAR(45);
DECLARE LDATE VARCHAR(45);
DECLARE LDATETIME DATETIME;
SELECT TIME FROM db.date_table INTO LTIME;
SELECT DATE FROM db.date_table INTO LDATE;
IF LENGTH(LDATE) = 9 AND SUBSTRING(LDATE,2,1) = '/'
THEN SET LDATETIME = CONCAT(SUBSTRING(LDATE,6,4),'-0',SUBSTRING(LDATE,1,1),'-',SUBSTRING(LDATE,3,2), ' ', LTIME);
ELSE IF LENGTH(LDATE) = 9 AND SUBSTRING(LDATE,3,1) = '/'
THEN SET LDATETIME = CONCAT(SUBSTRING(LDATE,6,4),'-',SUBSTRING(LDATE,1,2),'-0',SUBSTRING(LDATE,4,1), ' ', LTIME);
ELSE IF LENGTH(LDATE) = 10
THEN SET LDATETIME = CONCAT(SUBSTRING(LDATE,7,4),'-',SUBSTRING(LDATE,1,2),'-',SUBSTRING(LDATE,4,2), ' ', LTIME);
ELSE IF LENGTH(LDATE) = 8
THEN SET LDATETIME = CONCAT(SUBSTRING(LDATE,5,4),'-0',SUBSTRING(LDATE,1,1),'-0',SUBSTRING(LDATE,3,1), ' ', LTIME);
END IF;
END IF;
END IF;
END IF;
INSERT INTO db.date_table(table_name) VALUES (LDATETIME);
END //
CALL DateProc;
This seems to work and accounts for any variable date that may end up in my original date column.
Look at the value:
'2013-31-01 16:00:40'
That's trying to use a month of 31.
It's not clear whether that just means your test data is wrong, or whether you need to change these lines:
SELECT SUBSTRING(DATE,3,2) FROM db.test_table INTO LMONTH;
SELECT SUBSTRING(DATE,1,1) FROM db.test_table INTO LDAY;
to:
SELECT SUBSTRING(DATE,1,2) FROM db.test_table INTO LMONTH;
SELECT SUBSTRING(DATE,4,2) FROM db.test_table INTO LDAY;
Note the change from 1 to 2 for the substring starting at 1 anyway, and the change of the second starting position from 3 to 4. You want two-digit month and day values, right? If your data format is actually D/M/YYYY (i.e. only using two digits when they're required) then you won't be able to use fixed substring positions.
Somehow your LMONTH and LDAY values seem to be reversed as LMONTH is getting written as 31 and LDAY as 01 - which obviously incorrect. Check the data within your source table.