MySQL provides us with the ability to create scheduled events. These are tasks that run according to a specified schedule.
Often referred to simply as events, scheduled events can be created with the CREATE EVENT
statement, they can be modified with the ALTER EVENT
statement, and they can be removed with the DROP EVENT
statement.
Create an Event
As mentioned, we can create events with the CREATE EVENT
statement. Here’s a basic example:
CREATE EVENT Event1
ON SCHEDULE
EVERY 1 MINUTE
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event1');
That creates an event called Event1
that inserts a row in a table with the current date/time every minute. This assumes that the table exists and it has columns called EventTimestamp
and InsertedBy
.
Once we’ve run that statement, the event runs immediately. Then it runs every minute from then on. Whether it runs immediately will depend on the schedule though. For example, it won’t run immediately if we specify that it should only run at a specific time in the future.
If I select the contents of the table immediately, we can see that a row has already been inserted:
SELECT * FROM EventTest;
Result:
+---------------------+------------+ | EventTimestamp | InsertedBy | +---------------------+------------+ | 2024-03-31 09:00:59 | Event1 | +---------------------+------------+
And if we wait a few minutes more and then select the contents again we can see more rows:
SELECT * FROM EventTest;
Result:
+---------------------+------------+ | EventTimestamp | InsertedBy | +---------------------+------------+ | 2024-03-31 09:00:59 | Event1 | | 2024-03-31 09:02:00 | Event1 | | 2024-03-31 09:02:59 | Event1 | +---------------------+------------+
We can see that the event has updated the table every minute for the last three minutes as specified.
The IF NOT EXISTS
Clause
We can use an IF NOT EXISTS
clause to create the event only if it doesn’t exist. Or put another way, it won’t try to create the event if another event already exists with the same name.
CREATE EVENT IF NOT EXISTS Event1
ON SCHEDULE
EVERY 1 MINUTE
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event1');
We can run that code multiple times without error. If the event doesn’t exist, it will be created. If it does exist, it won’t throw an error, but a warning will be issued.
Here’s what that might look like:
Query OK, 0 rows affected, 1 warning (0.00 sec)
We can view the warning like this:
SHOW WARNINGS;
Output:
+-------+------+-------------------------------+ | Level | Code | Message | +-------+------+-------------------------------+ | Note | 1537 | Event 'Event1' already exists | +-------+------+-------------------------------+
As suspected, the event already exists.
Modify an Event
We can modify an event with the ALTER EVENT
statement. This allows us to modify the event without having to drop it and recreate it.
Let’s modify our event:
ALTER EVENT Event1
ON SCHEDULE
EVERY 1 HOUR
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event1');
Here, I changed the schedule of the event to run every hour instead of every minute.
Let’s check the contents of the table:
SELECT * FROM EventTest;
Result:
+---------------------+------------+ | EventTimestamp | InsertedBy | +---------------------+------------+ | 2024-03-31 09:00:59 | Event1 | | 2024-03-31 09:02:00 | Event1 | | 2024-03-31 09:02:59 | Event1 | | 2024-03-31 09:03:29 | Event1 | +---------------------+------------+
A row was inserted when I altered the event. The next row will be inserted an hour later (unless we change anything).
Dropping an Event
By default events are automatically dropped when they expire, but we can override this by specifying ON COMPLETION PRESERVE
.
As for events that haven’t expired, we can remove existing events with the DROP EVENT
statement:
DROP EVENT Event1;
Output:
Query OK, 0 rows affected (0.00 sec)
We can also include an IF EXISTS
clause so that no errors are returned if the event doesn’t exist:
DROP EVENT IF EXISTS Event1;
Output:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Although no errors are returned when it doesn’t exist, we do get a warning. We can use SHOW WARNINGS
to check the warning:
SHOW WARNINGS;
Output:
+-------+------+-----------------------------+ | Level | Code | Message | +-------+------+-----------------------------+ | Note | 1305 | Event Event1 does not exist | +-------+------+-----------------------------+
The warning simply informs us that the event didn’t exist when we tried to drop it.
Here’s what happens when we try to drop the non-existent event without using IF EXISTS
:
DROP EVENT Event1;
Output:
ERROR 1539 (HY000): Unknown event 'Event1'
We get an error.
Event Scheduling
We have various options for scheduling each event. In the above example we scheduled it to run every hour, which was nice and simple, but we can also be more specific with the schedule.
The following syntax shows the options with regards to scheduling of events:
AT timestamp [+ INTERVAL interval] ...
| EVERY interval
[STARTS timestamp [+ INTERVAL interval] ...]
[ENDS timestamp [+ INTERVAL interval] ...]
Basically what this means is that we have the option of specifying a specific timestamp, a repeating interval, or a repeating interval within a given window of time.
The interval
part can be any of the following:
YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE | DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND
The interval
part is preceded by the quantity. For example INTERVAL 5 WEEK
.
Run the Event Once in the Future
Here’s an example of creating an event that runs at a specified date/time:
CREATE EVENT Event2
ON SCHEDULE
AT '2024-03-31 09:07:51'
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event2');
Now let’s wait until that time and then query the table:
SELECT * FROM EventTest;
Result:
+---------------------+------------+ | EventTimestamp | InsertedBy | +---------------------+------------+ | 2024-03-31 09:00:59 | Event1 | | 2024-03-31 09:02:00 | Event1 | | 2024-03-31 09:02:59 | Event1 | | 2024-03-31 09:03:29 | Event1 | | 2024-03-31 09:07:51 | Event2 | +---------------------+------------+
We can see that the event ran at the specified time and inserted a new row into the table.
Schedule an Event Relative to the Current Date/Time
We can add an interval to the current timestamp in order to make the event run at a certain interval from the current date/time.
Example:
CREATE EVENT Event3
ON SCHEDULE
AT CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event3');
Now let’s wait until that time and then query the table:
SELECT * FROM EventTest;
Result:
+---------------------+------------+ | EventTimestamp | InsertedBy | +---------------------+------------+ | 2024-03-31 09:00:59 | Event1 | | 2024-03-31 09:02:00 | Event1 | | 2024-03-31 09:02:59 | Event1 | | 2024-03-31 09:03:29 | Event1 | | 2024-03-31 09:07:51 | Event2 | | 2024-03-31 09:13:45 | Event3 | +---------------------+------------+
A new row was inserted as expected.
The STARTS
and ENDS
Clauses
The STARTS
and ENDS
keywords can be used with the EVERY
clause in order to specify a time frame for which to start the event’s schedule.
Example:
CREATE EVENT Event4
ON SCHEDULE
EVERY 1 MINUTE
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
ENDS CURRENT_TIMESTAMP + INTERVAL 3 MINUTE
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event4');
Here I specified that the event should run every minute between a given window of time. The schedule starts one minute from the current time, and ends three minutes from the current time.
Now let’s wait until that time frame is finished and then query the table again:
SELECT * FROM EventTest;
Result:
+---------------------+------------+ | EventTimestamp | InsertedBy | +---------------------+------------+ | 2024-03-31 09:00:59 | Event1 | | 2024-03-31 09:02:00 | Event1 | | 2024-03-31 09:02:59 | Event1 | | 2024-03-31 09:03:29 | Event1 | | 2024-03-31 09:07:51 | Event2 | | 2024-03-31 09:13:45 | Event3 | | 2024-03-31 09:37:19 | Event4 | | 2024-03-31 09:38:19 | Event4 | | 2024-03-31 09:39:19 | Event4 | +---------------------+------------+
We can see that the event has run three times as specified.
Disabling an Event
We can use the DISABLE
keyword to disable an event. This allows the event to remain in the system, but prevents it from running, at least until it’s enabled.
Here’s an example of creating an event in disabled status:
CREATE EVENT Event5
ON SCHEDULE
EVERY 1 MINUTE
DISABLE
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event5');
So, although this event is set up to run every minute, it’s currently disabled and won’t run at all.
We can enable it with the ALTER EVENT
statement:
ALTER EVENT Event5
ENABLE;
In this case, enabling my event runs it immediately, and then it runs every minute thereafter. But this will depend on the event’s schedule.
We can use the ALTER EVENT
statement to disable the event again:
ALTER EVENT Event5
DISABLE;
Comments in Events
We can use the COMMENT
clause to provide comments in the event:
ALTER EVENT Event6
ON SCHEDULE
EVERY 1 HOUR
DISABLE
COMMENT 'Insert timestamp into table'
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event6');
The comment is a string literal and must be surrounded in quotes. It can be up to 64 characters long.
Persisting Expired Events
As mentioned, by default events are automatically dropped once they’ve expired, but we can override this by specifying ON COMPLETION PRESERVE
.
Example:
CREATE EVENT Event7
ON SCHEDULE
AT CURRENT_TIMESTAMP + INTERVAL 10 SECOND
ON COMPLETION PRESERVE
DO
INSERT INTO EventTest ( EventTimestamp, InsertedBy )
VALUES (NOW(), 'Event7');
Now when this event has run, it won’t be dropped. It will remain in the system in disabled status.
Get a List of Events
We can use the SHOW EVENTS
statement to return a list of events:
SHOW EVENTS;
Result:
+------+--------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+ | Db | Name | Definer | Time zone | Type | Execute at | Interval value | Interval field | Starts | Ends | Status | Originator | character_set_client | collation_connection | Database Collation | +------+--------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+ | test | Event5 | root@localhost | SYSTEM | RECURRING | NULL | 1 | MINUTE | 2024-03-31 11:30:27 | NULL | DISABLED | 1 | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci | | test | Event6 | root@localhost | SYSTEM | RECURRING | NULL | 1 | HOUR | 2024-03-31 11:37:11 | NULL | DISABLED | 1 | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci | | test | Event7 | root@localhost | SYSTEM | ONE TIME | 2024-03-31 11:45:36 | NULL | NULL | NULL | NULL | ENABLED | 1 | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci | +------+--------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
I ran that SHOW EVENTS
statement before Event7
ran, and so we can see that its status is ENABLED
.
Let’s run it again, after Event7
has finished running:
SHOW EVENTS;
Result:
+------+--------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+ | Db | Name | Definer | Time zone | Type | Execute at | Interval value | Interval field | Starts | Ends | Status | Originator | character_set_client | collation_connection | Database Collation | +------+--------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+ | test | Event5 | root@localhost | SYSTEM | RECURRING | NULL | 1 | MINUTE | 2024-03-31 11:30:27 | NULL | DISABLED | 1 | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci | | test | Event6 | root@localhost | SYSTEM | RECURRING | NULL | 1 | HOUR | 2024-03-31 11:37:11 | NULL | DISABLED | 1 | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci | | test | Event7 | root@localhost | SYSTEM | ONE TIME | 2024-03-31 11:45:36 | NULL | NULL | NULL | NULL | DISABLED | 1 | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci | +------+--------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
This time Event7
is in DISABLED
status. This is the event that I applied ON COMPLETION PRESERVE
to, so that it wouldn’t be dropped after it ran.
Actually, all events are currently disabled, due to the fact that I explicitly disabled the other two events.
Events and the sql_mode
System Variable
MySQL stores the sql_mode
system variable setting in effect when an event is created or altered, and always executes the event with this setting in effect, regardless of the current server SQL mode when the event begins executing.
So with this in mind, it should be safe to change the system’s sql_mode
without it inadvertently affecting any existing events.
More Information
See the MySQL documentation for the CREATE EVENT
statement for more information about creating events in MySQL. Also take a look at Event Scheduler Overview in the MySQL documentation.