SQL - Date field restriction (<=today) - mysql

I have a table with date which is entered when the member joins the club. The column is bog standard with DATE as type. No need to have time.
Basically I am trying to find a way that it can not be in the future?

You can use triggers. Below is a fully-functional tested example:
create table t(signup_date date not null);
delimiter //
create trigger ins_check_date before insert on t
for each row
begin
if (new.signup_date > current_date()) then
signal sqlstate '45000' set message_text = 'Date cannot be in the future';
end if;
end//
create trigger up_check_date before update on t
for each row
begin
if (new.signup_date > current_date()) then
signal sqlstate '45000' set message_text = 'Date cannot be in the future';
end if;
end//
delimiter ;
insert into t values (current_date());
insert into t values (current_date() - interval 1 day);
insert into t values (current_date() - interval 2 day);
insert into t values (current_date() + interval 1 day);
select * from t;
update t set signup_date = signup_date - interval 1 month
where signup_date = current_date() - interval 1 day;
update t set signup_date = signup_date + interval 1 month
where signup_date = current_date();
select * from t;

Related

Stored procedure MySQL 5.5.16

So I have in my database a table called "weeks" where I store every weeks of the years like so:
table weeks(id, year, num_week, date_min, date_max)
So for this week, the line look like this :
Note : My weeks starts on thursday and ends on Wenesday.
Since it is a pain inserting each weeks line by line, I want to create a stored procedure for this, here's what I came up with :
DELIMITER |
CREATE PROCEDURE proc_insert_weeks()
BEGIN
SELECT year, num_week, date_min, date_max INTO #year, #num_week, #date_min, #date_max
FROM weeks
ORDER BY date_min DESC LIMIT 1;
SET #date_min = DATE_ADD(#date_max INTERVAL 1 DAY);
SET #date_max = DATE_ADD(#date_min INTERVAL 6 DAY);
SET #year= YEAR(#date_min);
IF #num_week < 52 THEN SET #num_week = #num_week + 1;
ELSE SET #num_week = 1;
END IF;
INSERT INTO weeks (year, num_week, date_min, date_max)
VALUES (#year, #num_week, #date_min, #date_max);
END |
DELIMITER ;
So the idea was to take the last record of the table and add 1 week to the dates, but I can't even make it paste the creation of the procedure.
I get an error right after the SELECT query, can someone help me figure out what I am doing wrong ?
drop PROCEDURE proc_insert_weeks;
DELIMITER |
CREATE PROCEDURE proc_insert_weeks()
BEGIN
SELECT year, num_week, date_min, date_max INTO #year, #num_week, #date_min, #date_max
FROM weeks
ORDER BY date_min DESC LIMIT 1;
SET #date_min = DATE_ADD(#date_max, INTERVAL 1 DAY);
SET #date_max = DATE_ADD(#date_min, INTERVAL 6 DAY);
SET #year= YEAR(#date_min);
IF #num_week < 52 THEN SET #num_week = #num_week + 1;
ELSE SET #num_week = 1;
END IF;
INSERT INTO weeks (year, num_week, date_min, date_max)
VALUES (#year, #num_week, #date_min, #date_max);
END |
DELIMITER ;
You can try above code.
You made mistake in DATE_ADD function. You missed , in it.

Cannot declare variable in MySQL trigger

I am having difficulty creating the following trigger in MySQL:
CREATE TRIGGER TRG_COMPLETE_REMINDER
AFTER UPDATE ON reminders
FOR EACH ROW
BEGIN
DECLARE
new_date DATE;
IF (NEW.complete = 1 AND recurrence <> 'NONE') THEN
CASE recurrence
WHEN '1 WEEK' THEN
SELECT INTO new_date NEW.date + INTERVAL 7 DAY;
WHEN '1 MONTH' THEN
SELECT INTO new_date NEW.date + INTERVAL 1 MONTH;
WHEN '3 MONTH' THEN
SELECT INTO new_date NEW.date + INTERVAL 3 MONTH;
END CASE;
INSERT INTO reminders (description, date, userID, complete, recurrence)
VALUES (NEW.description, new_date, NEW.userID, 0, NEW.recurrence);
END IF;
END;
The issue seems to be occurring where I attempt to declare new_date. MySQL returns the following error message:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTO new_date NEW.date + INTERVAL 7 DAY' at line 8
I would like this trigger to create a new reminder record when the complete attribute changes to 1. The new record should have a date greater than the original record, depending on the value stored in recurrence.
Two corrections fixed the issue:
Correcting the order of clauses in the SELECT statement (i.e.
moving INTO new_date after SELECT new_date + INTERVAL 7 DAY)
Adding DELIMITER // before creating the trigger
Rather than use a variable you can use a CASE expression right in the INSERT statement:
CREATE TRIGGER TRG_COMPLETE_REMINDER
AFTER UPDATE ON reminders
FOR EACH ROW
BEGIN
IF (NEW.complete = 1 AND NEW.recurrence <> 'NONE') THEN
INSERT INTO reminders
(description,
date,
userID, complete, recurrence)
VALUES
(NEW.description,
CASE NEW.recurrence
WHEN '1 WEEK' THEN
NEW.date + INTERVAL 7 DAY
WHEN '1 MONTH' THEN
NEW.date + INTERVAL 1 MONTH
WHEN '3 MONTH' THEN
NEW.date + INTERVAL 3 MONTH
ELSE
NULL
END,
NEW.userID, 0, NEW.recurrence);
END IF;
END;
Best of luck.

MySQL - Duplicate PK when trigger is fired, but not when record is directly inserted

I have the following MySQL trigger. It is fired for an update on tbl_users. It will either update tbl_user_stats with some information if there already exists a record for that day, or it will insert a new record in tbl_user_stats if there is not a record for that day. action_date is the PK of tbl_user_stats and it holds the date.
drop trigger if exists trigger_update_user_stats_upgrades_and_downgrades$$
CREATE TRIGGER trigger_update_user_stats_upgrades_and_downgrades AFTER UPDATE ON tbl_users
FOR EACH ROW BEGIN
DECLARE date_now int;
IF NEW.premium <> OLD.premium THEN
SET date_now = (SELECT count(*) from tbl_user_stats WHERE DATE(action_date) = CURDATE());
IF date_now > 0 THEN
IF NEW.premium = 0 THEN
UPDATE tbl_user_stats SET downgrades = downgrades + 1 WHERE DATE(action_date) = CURDATE();
ELSE
UPDATE tbl_user_stats SET upgrades = upgrades + 1 WHERE DATE(action_date) = CURDATE();
END IF;
ELSE
IF NEW.premium = 0 THEN
INSERT INTO tbl_user_stats (action_date, downgrades) values (CURDATE() + INTERVAL 1 DAY, 1);
ELSE
INSERT INTO tbl_user_stats (action_date, upgrades) values (CURDATE() + INTERVAL 1 DAY, 1);
END IF;
END IF;
END IF;
END$$
Towards the bottom, I am inserting CURDATE() + INTERVAL 1 DAY for testing purposes. I am trying to simulate an update on tbl_users being performed on the next day. So if today is 11/22/13 I want to pretend that a record in tbl_users is being updated on 11/23/13. If the trigger works correctly, tbl_user_stats should have a new record inserted with the action_date of 11/23/13.
The problem is that when I try to update tbl_users, I get an error that says "Duplicate primary key 11/23/13". However, there is no primary key in tbl_user_stats set to 11/23/13. When I manually insert a new record into tbl_user_stats using 11/23/13 as the PK, there is no problem. The problem only arises when I try to update tbl_users. Why am I getting the Duplicate primary key error?
I have reproduced this error.
This occurs because you're checking condition of "date_now" is checking for CURDATE()
when you're inserting with CURDATE() + INTERVAL 1 DAY
To simulate the next day, you should change all your date comparisons to CURDATE() + INTERVAL 1 DAY
sqlFiddle code with all CURDATE() changed to CURDATE + INTERVAL 1 DAY gets rid of the error.
To reproduce error, just change
SET date_now = (SELECT count(*) from t2 WHERE DATE(start_date) = CURDATE()+ INTERVAL 1 DAY);
to
SET date_now = (SELECT count(*) from t2 WHERE DATE(start_date) = CURDATE());`

Un-group a row into many rows based on date parts

I'm using MySql and I have a Policy table with StartDate and EndDate columns.
How can I write a SELECT query to give me a new row for each month in the date range between these 2 columns.
For example if I have a policy with:
Id StartDate EndDate
123456 2011-05-25 2011-07-26
I would want to see:
Id PolicyId StartDate EndDate
1 123456 2011-05-25 2011-06-24
2 123456 2011-06-25 2011-07-24
3 123456 2011-07-25 2011-07-26
I'm not sure about performance because I'm not much experienced with stored procedures so there might be a better approach. Also, you might want to change the structure of the temporary table (aka. PolicyList). Anyway…
This can also be converted into before/after triggers instead of executing it each time.
DROP PROCEDURE IF EXISTS CreatePolicyList;
DELIMITER //
CREATE PROCEDURE CreatePolicyList()
BEGIN
DECLARE origId, done INT DEFAULT 0;
DECLARE startD, endD DATE;
DECLARE cur CURSOR FOR
SELECT id, StartDate, EndDate FROM Policy;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS PolicyList;
CREATE TEMPORARY TABLE PolicyList (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
PolicyId INT(11) NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
OPEN cur;
recLoop: LOOP
FETCH cur INTO origId, startD, endD;
IF (done)
THEN LEAVE recLoop;
END IF;
-- following is an alternative to keep records like
-- "2011-05-25, 2011-06-25" in a single record
-- WHILE startD < DATE_SUB(endD, INTERVAL 1 MONTH) DO
WHILE startD < DATE_ADD(DATE_SUB(endD, INTERVAL 1 MONTH), INTERVAL 1 DAY) DO
INSERT INTO PolicyList (PolicyId, StartDate, EndDate)
VALUES (origId, startD,DATE_SUB(
DATE_ADD(startD, INTERVAL 1 MONTH),
INTERVAL 1 DAY
));
SET startD = DATE_ADD(startD, INTERVAL 1 MONTH);
END WHILE;
IF startD >= DATE_SUB(endD, INTERVAL 1 MONTH) THEN
INSERT INTO PolicyList (PolicyId, StartDate, EndDate)
VALUES (origId, startD, endD);
END IF;
END LOOP;
CLOSE cur;
END //
CALL CreatePolicyList;
and then query:
SELECT * FROM PolicyList
ORDER BY PolicyId, StartDate;

MySQL query to show all the hours within the next month

How would I put together a query to display all of the hours in the next week as I want to compare a timetable against this for appointment purposes.
Thanks for any help!
edit--
the expected result would be great as between 9 to 5
| client_date | client_time |
10/01/2010 09:00:00
10/01/2010 10:00:00
10/01/2010 11:00:00
10/01/2010 12:00:00
10/01/2010 13:00:00
10/01/2010 14:00:00
10/01/2010 15:00:00
10/01/2010 16:00:00
10/01/2010 17:00:00
You will need to create a table to store the date and time values.
CREATE TABLE calendarhours (caldaytime DATETIME);
You will then need to create a stored procedure to loop through the two dates and insert the date time values for the time sheet times into the table.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `timesheetdays`(startdate DATETIME, enddate DATETIME)
BEGIN
DECLARE tempdate DATETIME;
DELETE FROM `calendarhours`;
-- set the temp date to 9am of the start date
SET tempdate = DATE_ADD(DATE(startdate), INTERVAL '0 9' DAY_HOUR);
-- while the temp date is less than or equal to the end date, insert the date
-- into the temp table
WHILE ( tempdate <= enddate ) DO
BEGIN
-- insert temp date into temp table
INSERT INTO `calendarhours` (caldaytime) VALUES (tempdate);
-- increment temp date by an hour
SET tempdate = DATE_ADD(tempdate, INTERVAL '0 1' DAY_HOUR);
-- if the temp date is greater than 5 PM (17:00) then increment to the next day
IF TIMEDIFF(tempdate, DATE_ADD(DATE(tempdate), INTERVAL '0 17' DAY_HOUR)) > 0 THEN
BEGIN
-- increment to the next day
SET tempdate = DATE_ADD(DATE(tempdate), INTERVAL '1 9' DAY_HOUR);
-- for business purposes, if the day is a Saturday or a Sunday increment
-- until we reach Monday
WHILE ( DAYNAME(tempdate) = 'Saturday' OR DAYNAME(tempdate) = 'Sunday' ) DO
BEGIN
SET tempdate = DATE_ADD(DATE(tempdate), INTERVAL '1 9' DAY_HOUR);
END;
END WHILE;
END;
END IF;
END;
END WHILE;
-- return all the inserted date and times
SELECT * FROM calendarhours ORDER BY caldaytime;
END
This procedure will then loop through the two dates, starting from 9 am each day and finishing at 5pm each day (17:00). When the time reaches 18:00, the procedure increments to the next day and starts again at 9 am.
If you are doing a standard business week timesheet, then if the day is equal to Saturday or Sunday, it will increment until it reaches Monday.
To test this I used the following statements:
CALL `timesheetdays`(NOW(), DATE_ADD(DATE(NOW()), INTERVAL '5 0' DAY_HOUR));
SELECT * FROM `calendarhours`;
This tests the procedure from today to 5 days from today and shows the hours as required. The first statement adds the records to the table and then returns the records, the second statement returns the records from the table.
you can use a temporary table in a stored procedure.
DELIMITER ;;
DROP PROCEDURE IF EXISTS ListHours ;;
CREATE PROCEDURE ListHours()
BEGIN
DECLARE curDT DATETIME;
DECLARE today DATETIME ;
DECLARE nextSaturday DATETIME;
DECLARE nextSunday DATETIME;
DECLARE iterDate DATETIME;
DECLARE iterDateTime DATETIME;
DECLARE iterBound DATETIME;
DECLARE resDate DATETIME;
DECLARE resTime DATETIME;
DECLARE delta INT;
DROP TABLE IF EXISTS tempNextWeek;
CREATE TEMPORARY TABLE IF NOT EXISTS tempNextWeek
(
client_date VARCHAR(20),
client_time VARCHAR(20)
);
DELETE FROM tempNextWeek;
SET curDT = NOW();
SET today = ADDTIME(SUBTIME(curDT , TIME(curDT)) , '9:0:0');
SET delta = 8 - DAYOFWEEK(today);
SET nextSunday = ADDDATE(today , INTERVAL delta DAY);
SET nextSaturday = ADDTIME(nextSunday , '6 0:0:0');
-- select today , delta , nextSaturday , nextSunday ;
SET iterDate = nextSunday;
WHILE iterDate <= nextSaturday DO
SET iterDateTime = iterDate;
SET iterBound = ADDTIME(iterDateTime, '8:0:0');
WHILE iterDateTIme <= iterBound DO
INSERT tempNextWeek (client_date, client_time) VALUE ( DATE_FORMAT(iterDateTime, '%Y-%m-%d'), DATE_FORMAT(iterDateTime, '%H:%i:%s') );
SET iterDateTime = ADDTIME(iterDateTime , '1:0:0');
END WHILE;
SET iterDate = ADDTIME(iterDate , '1 0:0:0');
END WHILE ;
SELECT * FROM tempNextWeek;
-- drop table if exists tempNextWeek;
END;;
DELIMITER ;
CALL ListHours();