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.
Related
I have a query :
insert into fookoo_business
select stat_date, sum(spend), sum(revenue)
from hooloo_business;
that i want to run for each date from '2017-01-20' until yesterday (it means the query will run 434 times if we're at 01/04/2018), for each date separately
(in a loop).
how can i create a loop in Mysql to do it for me?
I have tried:
creating procedure for the query select #stat_date, sum(spend), sum(revenue)
I called 'query'
then :
CREATE PROCEDURE loop_procedure()
BEGIN
SET #stat_date='2018-03-20';
CALL 'query';
REPEAT
SET #stat_date = #stat_date + INTERVAL 1 DAY;
UNTIL #stat_date = CURDATE() END REPEAT;
END
eventually i've used the following logic within a stored procedure to fetch the data:
PROCEDURE `x_monitoring_loop`()
BEGIN
DECLARE i INT;
DECLARE len INT;
SET len = 434;
SET i = 0;
WHILE (i < len) DO
SET #stat_date= CURDATE()-INTERVAL 1 DAY;
SET #stat_date= #stat_date- INTERVAL i DAY;
Insert query;
SET i = i +1;
END WHILE;
This way the query ran 434 times for each day, beginning at current date - 1 day.
I do not know why you want to use a procedure,I think we can just use a query sql to do it:
INSERT INTO fookoo_business
SELECT stat_date, SUM(spend), SUM(revenue)
FROM hooloo_business
WHERE stat_date BETWEEN STR_TO_DATE('2017-01-02', '%Y-%m-%d') -- start date
AND DATE_SUB(NOW(), INTERVAL 1 DAY) -- end date
GROUP BY stat_date;
I'm currently using DATE_ADD(date,INTERVAL expr type) to set a due date as a trigger in a mySQL Database.
What I'm wanting to know is if it is possible to skip weekends (Saturday, Sunday) as part of the trigger.
You'd have to create an own function for doing that. You can look how to do that in this answer, for example (just use function instead of procedure). As for how to write such a function, here's a working algorithm. The code is quite straightforward: it loops through days and skips weekends.
CREATE FUNCTION `DAYSADDNOWK`(addDate DATE, numDays INT) RETURNS date
BEGIN
IF (WEEKDAY(addDate)=5) THEN
SET addDate=DATE_ADD(addDate, INTERVAL 1 DAY);
END IF;
IF (WEEKDAY(addDate)=6) THEN
SET addDate=DATE_ADD(addDate, INTERVAL 1 DAY);
END IF;
WHILE numDays>0 DO
SET addDate=DATE_ADD(addDate, INTERVAL 1 DAY);
IF (WEEKDAY(addDate)=5) THEN
SET addDate=DATE_ADD(addDate, INTERVAL 1 DAY);
END IF;
IF (WEEKDAY(addDate)=6) THEN
SET addDate=DATE_ADD(addDate, INTERVAL 1 DAY);
END IF;
SET numDays=numDays-1;
END WHILE;
RETURN addDate;
END
Currently SELECT DAYSADDNOWK(CURDATE(), 5) yields 2016-03-07, which is correct.
Of course you only can use it with days, so no arbitrary interval, but your question mentioned date datatype, and I don't quite see how one could add a month not counting working days.
This function simply creates a list of dates starting at the date given in the arguments, and then figures out which date is x number of days (the interval) out while disregarding days 1 and 7 (which are Sunday and Saturday respectively on SQL Server).
CREATE FUNCTION [dbo].[udf_days_add_no_wknd]
(
#start_date date
, #interval int
)
RETURNS date
AS
BEGIN
declare #answer date
; with dates as
(
select #start_date as date_val
union all
select dateadd(d, 1, date_val) as date_val
from dates
where date_val < dateadd(d, #interval * 10, #start_date)
)
, final as
(
select top 1 lead(ld.date_val, #interval, NULL) over (order by ld.date_val asc) as new_date_val
from dates as ld
where 1=1
and datepart(dw, ld.date_val) not in (1,7) --eliminating weekends
)
select #answer = (select new_date_val from final)
return #answer
END
It is worth nothing that this solution is dependent on having SQL Server 2012 or later, considering the use of the lead() function.
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;
What I am trying to do, is to set beginning of time interval, if that is not correctly set into stored procedure. However, it somehow does not work very well..
This is my code:
CREATE PROCEDURE intervals_generator (IN start DATETIME, IN ending DATETIME, IN intervalis INT)
BEGIN
-- temp values
DECLARE next_date DATETIME;
-- result temp values
DECLARE start_temp DATETIME;
DECLARE ending_temp DATETIME;
-- date formatting variables
DECLARE year CHAR(20);
DECLARE month CHAR(20);
DECLARE day CHAR(20);
DECLARE new_start CHAR(20);
-- SET starting date if is incorrect DATE_FORMAT(NOW(), '%d %m %Y')
SET year := DATE_FORMAT(start, '%Y');
SET month := DATE_FORMAT(start, '%c');
SET day := DATE_FORMAT(start, '%e');
IF intervalis = '1_day' THEN
BEGIN
SET new_start := year+' '+month+' '+day+' 00:00:00';
END;
ELSEIF intervalis = '1_month' THEN
BEGIN
SET new_start := year+' '+month+' 1 00:00:00';
END;
ELSEIF intervalis = '1_quarter' THEN
BEGIN
IF MONTH(start) IN (2, 3) THEN
SET month := 1;
ELSEIF MONTH(start) IN (5, 6) THEN
SET month := 4;
ELSEIF MONTH(start) IN (8, 9) THEN
SET month := 7;
ELSEIF MONTH(start) IN (11, 12) THEN
SET month := 10;
END IF;
SET new_start := year+' '+month+' 1 00:00:00';
END;
ELSEIF intervalis = '1_year' THEN
BEGIN
SET new_start := year+' 1 1 00:00:00';
END;
END IF;
SET start := STR_TO_DATE(new_start, '%Y %c %e %h:%i:%s');
SELECT year, month, day, start;
DROP TEMPORARY TABLE IF EXISTS intervals_result;
END//
DELIMITER ;
I have tried many different formattings settings and functions, but the output is still wrong, like this:
mysql> CALL intervals_generator('2013-02-01 00:00:00', '2015-12-31 00:00:00', '1_year');
+------+-------+------+---------------------+
| year | month | day | start |
+------+-------+------+---------------------+
| 2013 | 2 | 1 | 2016-00-00 00:00:00 |
+------+-------+------+---------------------+
1 row in set (0.02 sec)
Query OK, 0 rows affected, 1 warning (0.02 sec)
I really dont understand why output is "2016-00-00" instead of "2013-01-01". year, month and day variables are defined as CHAR and also function that extracts them from datetime should be returning CHAR. And function STR_TO_DATE should also been taking CHAR format, so it is a mystery for me.
If anyone has some idea, please give me hint.
If you work in DATEs instead of strings, you can make use of MySQL's date functions and operators and make everything a whole lot simpler... but not too simple because this is MySQL.
The problem with MySQL and dates is its date functionality is a real mish-mash that sometimes works with DATEs, sometimes with strings, sometimes with integers, and is missing basic functionality. It lacks a simple function to set a piece of a date; there's no function to change the MONTH part of a DATE to February. There's not even a good way to make a date from the year, month and day, closest thing you get is MAKEDATE() which takes a year and the day of the year (?!). Fortunately, DATEs in MySQL respond to math operations and it's better than messing with strings.
If you have, for example, 2013-02-12 and want 2013-02-01 you have to first make a new date with just the year using MAKEDATE, then add the month part.
-- 2013-01-01
SET new_date := MAKEDATE(YEAR(old_date), 1);
-- 2013-02-01
-- Since MONTH returns from 1 to 12, you need to take away one.
SET new_date := new_date + (INTERVAL MONTH(old_date) - 1) MONTH;
After chopping out all the unused variables, changing to date math, and using the CASE statement instead of a big IF/ELSE chain, we get this:
CREATE PROCEDURE intervals_generator (IN start_date DATE, IN intervals TEXT)
BEGIN
DECLARE new_start DATE;
CASE intervals
WHEN '1_day' THEN
-- Nothing to do, DATE has already truncated the time portion.
SET new_start := start_date;
WHEN '1_month' THEN
-- Set to the year and month of the start date
SET new_start := MAKEDATE(YEAR(start_date), 1) + INTERVAL (MONTH(start_date) - 1) MONTH;
WHEN '1_quarter' THEN
BEGIN
-- Set to the year and month of the start date
SET new_start := MAKEDATE(YEAR(start_date), 1) + INTERVAL (MONTH(start_date) - 1) MONTH;
-- Subtract the necessary months for the beginning of the quarter
SET new_start := new_start - INTERVAL (MONTH(new_start) - 1) % 3 MONTH;
END;
WHEN '1_year' THEN
-- Set the date to the first day of the year
SET new_start := MAKEDATE(YEAR(start_date), 1);
END CASE;
SELECT new_start;
END//
Try it out.
This statement is not doing what you expect:
SET new_start := year+' '+month+' '+day+' 00:00:00';
In MySQL, the + operator does addition. That's it, not concatenation.
I think you intend:
SET new_start := concat(year, ' ', month, ' ', day, ' 00:00:00');
I haven't looked at the rest of the logic to see if it makes sense, but this is one glaring problem.
Instead of slicing and building new date from parts(concatenating) which is error-prone you can use built-in DATE_FORMAT:
SqlFiddleDemo
SET #date = '2013-05-03 10:05:00';
SELECT CAST(#date AS DATETIME) AS Date,
DATE_FORMAT(#date ,'%Y-01-01 00:00:00') AS Year_Allign,
CASE EXTRACT(QUARTER FROM #date)
WHEN 1 THEN DATE_FORMAT(#date ,'%Y-01-01 00:00:00')
WHEN 2 THEN DATE_FORMAT(#date ,'%Y-04-01 00:00:00')
WHEN 3 THEN DATE_FORMAT(#date ,'%Y-07-01 00:00:00')
WHEN 4 THEN DATE_FORMAT(#date ,'%Y-10-01 00:00:00')
ELSE NULL END AS Quarter_Allign,
DATE_FORMAT(#date ,'%Y-%m-01 00:00:00') AS Month_Allign,
DATE_FORMAT(#date ,'%Y-%m-%d 00:00:00') AS Day_Allign;
SqlFiddleDemo2
SET #date = '2013-05-03 10:05:00';
SET #allign = '1_QUARTER';
SELECT
CAST(#date AS DATETIME) AS Date,
CASE #allign
WHEN '1_YEAR' THEN DATE_FORMAT(#date ,'%Y-01-01 00:00:00')
WHEN '1_QUARTER' THEN (CASE EXTRACT(QUARTER FROM #date)
WHEN 1 THEN DATE_FORMAT(#date ,'%Y-01-01 00:00:00')
WHEN 2 THEN DATE_FORMAT(#date ,'%Y-04-01 00:00:00')
WHEN 3 THEN DATE_FORMAT(#date ,'%Y-07-01 00:00:00')
WHEN 4 THEN DATE_FORMAT(#date ,'%Y-10-01 00:00:00')
ELSE NULL END)
WHEN '1_MONTH' THEN DATE_FORMAT(#date ,'%Y-%m-01 00:00:00')
WHEN '1_DAY' THEN DATE_FORMAT(#date ,'%Y-%m-%d 00:00:00')
ELSE NULL
END AS Alligned;
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();