I have a table tblMatch :
+---------+---------------------+------------------+-----------+
| ID | start_date | end_date | status |
+---------+---------------------+------------------+-----------+
| 1 | 2017-12-09 03:23 | 2017-12-10 03:23 | 1 |
+---------+---------------------+------------------+-----------+
| ... | ... | ... | 1 |
+---------+---------------------+------------------+-----------+
| 1000000 | 2017-12-22 15:12 | 2017-12-30 15:12 | 1 |
+---------+---------------------+------------------+-----------+
When I insert a row, I create one event too.
Event will change status to 0 if the match is ended.
CREATE EVENT test_event_increment_number
ON SCHEDULE AT end_date
ON COMPLETION NOT PRESERVE
DO
UPDATE tblMatch SET status = 0 WHERE ID = increment_number;
If tblMatch has 100 million matches :
Does it effect server performance?
Is it bad or good idea to create a lot of events?
Create just 1 event that runs daily and closes the matches expiring that day.
CREATE EVENT test_event
ON SCHEDULE AT 1 every day
STARTS (TIMESTAMP(CURRENT_DATE) + INTERVAL 1 DAY)
DO
UPDATE tblMatch SET status = 0 WHERE end_date = CURRENT_DATE;
UPDATE
If you want to time your events at a minute level, then either change the frequency of your event to minute level and use minute level when determining if a match needs closing, or completely drop the status field and just use the end_date field's value compared to now() to determine if the event is closed. The latter is a better way.
I Create 25 events is different time. After a while, the database takes all the resources, and reboots.
CREATE EVENT `auction_event_46709`
ON SCHEDULE EVERY 5 MINUTE STARTS '2017-12-07 10:23:03'
ON COMPLETION NOT PRESERVE ENABLE
DO CALL auction_update_price(46709)
Procedure:
CREATE PROCEDURE `auction_update_price`( IN p_id INT )
BEGIN
DECLARE cur_price INT;
DECLARE stp_price INT;
DECLARE st_price INT;
DECLARE str_price INT;
SELECT current_price, step_price, stop_price, start_price INTO cur_price, stp_price, st_price, str_price FROM product_to_auction WHERE product_id = p_id;
IF( cur_price - stp_price > st_price ) THEN
UPDATE product_to_auction SET current_price = current_price - step_price WHERE current_price > stop_price AND product_id = p_id;
ELSE
UPDATE product_to_auction SET current_price = str_price WHERE product_id = p_id;
END IF;
END
How to Fix?
Don't use events at all for this sort of operation. Certainly don't use many events for it. Instead, use a query (or view) that takes your end_date into account and determines your status value dynamically based on date. For example, to retrieve a particular item by id, do this.
SELECT id, start_date, end_date
CASE WHEN end_date <= CURDATE() THEN 0 ELSE status END AS status
FROM tblMatch
WHERE id = something
This query returns the row from the table, along with the status value based on the moment you run the query. (I set it up so items with status = 0 are always marked as expired never mind the current time.)
If you want all the items with status 1 (meaning non expired) do this:
SELECT id, start_date, end_date, 1 AS status
FROM tblMatch
WHERE end_date < CURDATE ()
AND status = 1
If you MUST use an event, you can run it once a day, sometime after midnight to reset the status columns of all expiring rows to 0, with a query like this.
UPDATE tblMatch SET status = 0 WHERE status = 1 AND end_date < CURDATE();
(I prefer to run daily update queries shortly after 03:00 local time. Why? I'm located in the USA, and our daylight saving time switchover is done, twice a year, at 02:00 local time. Doing daily updates after 03:00 ensures they'll still work properly on switchover days. )
For these queries to be efficient, you need a compound index on (status, end_date)
Related
This question already has answers here:
Check overlap of date ranges in MySQL
(8 answers)
Closed 4 years ago.
I know this can be accomplished on the backend - but I'm wondering if there's any native, or efficient MySQL function that can be used to check if a given time variable falls within a range covered by two time variables (a start_time, and an end_time).
I have a database currently set up which looks like the following;
+----+--------+------------+----------+
| id | job_id | start_time | end_time |
+----+--------+------------+----------+
| 1 | 40 | 13:00:00 | 14:00:00 |
| 2 | 44 | 14:45:00 | 15:00:00 |
| 3 | 45 | 15:10:00 | 15:30:00 |
+----+--------+------------+----------+
The backend accepts a start_time, and an end_time with a job_id - and then it looks to see if it can be fit in anywhere. So for example, given a start_time of 13:30:00, and an end_time of 13:45:00, the backend should reject this job request as there is no time available for it (it would overlap with the entry at id 1.)
However, if a job is submitted with a start_time of 14:10:00, and an end time of 14:20:00, it should be accepted - as this does not overlap with any existing tasks.
The following query is great to tell if a job can be submitted for say, 13:00:00 until 14:00:00 (an exact duplicate of id 1);
SELECT * WHERE start_time >= '13:00:00' AND end_time <= '14:00:00';
But if the start_time becomes 13:01:00, then the query falls down - as the start_time is less than 13:01:00, at 13:00:00. So it'll get approved for insertion, as the above query will return no overlapping results.
If we change the query to an OR clause on end_time, then literally any job that doesn't end before 14:00:00 would be rejected.
Can anyone suggest a simple way of taking an input variable of a time type, and checking if it falls within range of all available start_time, and end_time variables as noted in the db above?
I would suggest you to check in in one of the following ways: hours/minutes/seconds
SELECT * FROM timetableTemp
WHERE HOUR(TIMEDIFF(start_time, end_time))>=1
AND start_time >=start_time
OR use BETWEEN function
SELECT * FROM timetableTemp
WHERE '13:00:00' BETWEEN start_time AND end_time
support:
SELECT SECOND(TIMEDIFF("13:10:11", "13:10:10")) AS Seconds; -- 1
SELECT MINUTE(TIMEDIFF("13:10:11", "13:10:10")) AS Minutes; -- 0
SELECT HOUR(TIMEDIFF("13:00:00", "14:00:00")) AS Hours; -- 1
Let's suppose you have a new job with start of new_start and end of new_end. Assume your existing list is in order of time of each job entry, and they are all non-overlapping.
What you'd need to check to see if the new job fits in the list without overlap would be:
new_end is less than the start_time of the first list entry, in
which case the new entry fits at the beginning, OR
new_start is
greater than the end_time of the last list entry, in which case the
new entry fits at the end, OR
For some in-between list entry,
new_start is greater than the end_time and new_end is less than
the start_time of the following list entry, in which case the new
entry fits in between this entry and the next.
I am trying to create a report schedule in mysql, but the query depends on the local variable fdate. How can I have mysql load the fdate variable and then run the query in the 2nd event? Thanks
CREATE EVENT fdate
ON SCHEDULE
EVERY 1 DAY
starts '2018-03-26 07:30:00'
Do
set #fdate = 2018031; #EOM prior month
create event report_1 on SCHEDULE
EVERY 1 day
starts '2018-03-26 07:31:00'
DO
<Here is the Query depending up fdate>
I think each event executes in a new session, and user-defined variables have session scope. So you can't really use those variables in events and have their value survive from one event execution to another.
Here's a test to show it:
mysql> create table log ( i int );
mysql> create event e on schedule every 1 minute do
-> insert into log set i = #a := coalesce(#a+1, 1);
If the #a variable retains its value from one event execution to the next, we'll get a series of incrementing values in my log table.
A few minutes later, we see that we don't get incrementing values. Each execution starts the counter over at 1.
mysql> select * from log;
+------+
| i |
+------+
| 1 |
| 1 |
| 1 |
+------+
But you can DECLARE a local variable, and use it in a compound statement body for an event.
CREATE EVENT fdate
ON SCHEDULE
EVERY 1 DAY
STARTS '2018-03-26 07:30:00'
DO BEGIN
DECLARE fdate INT;
SET fdate = DATE_FORMAT(LAST_DAY(CURDATE() - INTERVAL 1 MONTH), '%Y%m%d');
# following query uses fdate
END
After trawling through the web and still not finding any simple answers I'm hoping someone can advise on this.
All I want to do is insert a new row into a table if a value from that table is not null. In this case, I have a table called "Events".
Events Table
Event_ID | Event_Name | Event_Interval | Event_Date | Event_Interval | Event_Repeat
-----------------------------------------------------------------------------------
1 My Event 14 2017-04-04 14 Completion
There are two conditions on which I'm working towards; best described using a bit of pseudocode:
IF EVENT_INTERVAL != NULL THEN
IF EVENT_REPEAT = "DUE" THEN
DATE_ADD(EVENT_DATE, INTERVAL EVENT_INTERVAL DAY)
ELSE
DATE_ADD(NOW(), INTERVAL EVENT_INTERVAL DAY)
END IF
END IF
So: when someone completes an event, if the interval value is not null, it inserts a new row to the table where it adds the interval value to either the event date or the current date, depending on the value in "Event_Repeat".
So far I've got to :
SELECT
(IF(ISNULL((SELECT e.Event_Interval from events e where e.Event_ID = 1)) = 1, "NULL", "NOT NULL"))
This works absolutely fine and can distinguish whether the interval value is NULL or not. I've tried to incorporate/substitute an insert statement into here but as of yet no luck.
If anyone can help to structure this properly (or better!) please feel free as I'm still finding my way around queries etc.
Thanks in advance.
you can create SP as
CREATE PROCEDURE update_event
#event_id int
AS
BEGIN
DECLARE #event_repeat_temp nvarchar(200)
SELECT #event_repeat_temp = event_repeat FROM event_table WHERE event_id = #event_id
PRINT #event_repeat_temp
IF #event_repeat_temp = 'Due'
BEGIN
INSERT INTO event_table (event_id , event_name, event_interval, event_date, interval,event_repeat) VALUES (2,'My Event',14,'completion')
END
ELSE
BEGIN
//whatever you want
END
END
and call this SP as below
EXEC update_event #event_id =1
I have a table with an ID, a datetime that something occurred, and a description of what it was.
With that table, I've been able to query for the number of times that something occurs during that day of the week with the following:
mysql> select distinct(DATE(HEADER_DATE)) from emails_inbound where WEEKDAY(HEADER_DATE) = 6;
+---------------------+
| (DATE(HEADER_DATE)) |
+---------------------+
| 2014-09-21 |
| 2014-09-28 |
| 2014-10-05 |
+---------------------+
3 rows in set (0.00 sec)
What I'd like to see is a count of the number of days of the week (Sundays, in this example) that nothing occured (no result returned for that day) starting since a particular day.
In other words, how many Sundays since 9/1/2014 contain zero results?
Many thanks in advance!
Just put together a temp table that will give you a row for each day in your desired date range, and then LEFT JOIN that against your main table and GROUP BY the weekday.
I do not have MySQL at my disposal and this seems to be difficult to do in SQLFiddle (mix schema & non-schema statements), but the following code should work as-is or with very minor tweaks. It is important to note that I assume that the ID column of your emails_inbound table is called id; if it is something else, change e.id to e.whatever.
drop temp table if exists t_tmp;
create temp table t_tmp (
my_date date
) engine=memory;
declare v_startDate date;
declare v_endDate date;
set v_startDate = '10-1-2014'; -- YOUR START DATE
set v_endDate = '10-10-2014'; -- YOUR END DATE
while (v_startDate < v_endDate) do
insert into t_tmp values (v_startDate);
set v_startDate = v_startDate + interval 1 day;
end while;
select weekday(date(e.header_date)) as day_of_week,
count(e.id) as number_of_days_when_this_event_happened,
sum(case when e.id is not null then 0 else 1 end) as number_of_days_when_this_event_did_not_happen
from t_tmp x
left join emails_inbound e on date(e.header_date) = x.my_date
group by weekday(date(e.header_date))
drop temp table t_tmp;
I have a log table with a date field called logTime. I need to show the number of rows within a date range and the number of records per day. The issue is that i still want to show days that do not have records.
Is it possible to do this only with SQL?
Example:
SELECT logTime, COUNT(*) FROM logs WHERE logTime >= '2011-02-01' AND logTime <= '2011-02-04' GROUP BY DATE(logTime);
It returns something like this:
+---------------------+----------+
| logTime | COUNT(*) |
+---------------------+----------+
| 2011-02-01 | 2 |
| 2011-02-02 | 1 |
| 2011-02-04 | 5 |
+---------------------+----------+
3 rows in set (0,00 sec)
I would like to show the day 2011-02-03 too.
MySQL will not invent rows for you, so if the data is not there, they will naturally not be shown.
You can create a calendar table, and join in that,
create table calendar (
day date primary key,
);
Fill this table with dates (easy with a stored procedure, or just some general scripting), up till around 2038 and something else will likely break unitl that becomes a problem.
Your query then becomes e.g.
SELECT logTime, COUNT(*)
FROM calendar cal left join logs l on cal.day = l.logTime
WHERE day >= '2011-02-01' AND day <= '2011-02-04' GROUP BY day;
Now, you could extend the calendar table with other columns that tells you the month,year, week etc. so you can easily produce statistics for other time units. (and purists might argue the calendar table would have an id integer primary key that the logs table references instead of a date)
In order to accomplish this, you need to have a table (or derived table) which contains the dates that you can then join from, using a LEFT JOIN.
SQL operates on the concept of mathematical sets, and if you don't have a set of data, there is nothing to SELECT.
If you want more details, please comment accordingly.
I'm not sure if this is a problem that should be solved by SQL. As others have shown, this requires maintaining a second table that contains the all of the individual dates of a given time span, which must be updated every time that time span grows (which presumably is "always" if that time span is the current time.
Instead, you should use to inspect the results of the query and inject dates as necessary. It's completely dynamic and requires no intermediate table. Since you specified no language, here's pseudo code:
EXECUTE QUERY `SELECT logTime, COUNT(*) FROM logs WHERE logTime >= '2011-02-01' AND logTime <= '2011-02-04' GROUP BY DATE(logTime);`
FOREACH row IN query result
WHILE (date in next row) - (date in this row) > 1 day THEN
CREATE new row with date = `date in this row + 1 day`, count = `0`
INSERT new row IN query result AFTER this row
ADVANCE LOOP INDEX TO new row (`this row` is now the `new row`)
END WHILE
END FOREACH
Or something like that
DECLARE #TOTALCount INT
DECLARE #FromDate DateTime = GetDate() - 5
DECLARE #ToDate DateTime = GetDate()
SET #FromDate = DATEADD(DAY,-1,#FromDate)
Select #TOTALCount= DATEDIFF(DD,#FromDate,#ToDate);
WITH d AS
(
SELECT top (#TOTALCount) AllDays = DATEADD(DAY, ROW_NUMBER()
OVER (ORDER BY object_id), REPLACE(#FromDate,'-',''))
FROM sys.all_objects
)
SELECT AllDays From d