/var/log/jsoizo

メモ帳 技術とか趣味とか

MySQLのイベントスケジューラで時間帯指定のスケジュール実行を設定したい

業務上よくあるシチュエーションとして 午前9時から18時までの毎時0分平日の19時 みたいなスケジュールを組みたいことがある。
cronなんかだと 0 9-18 * * *0 19 * * 1-5 のように書けば良いが、MySQLのイベントスケジューラの場合ではどう書くか。

サマリ

特定の時間帯のみや特定の曜日のみなどの複雑な条件は設定することができないため、スケジューラが実行する処理の中で時刻による実施要否判定を行う必要がある。以下のように書けばよい。

DELIMITER |
CREATE EVENT {{イベント名}}
ON SCHEDULE {{任意の定期実行スケジュール}}
DO
BEGIN
    SET @NOW = CURRENT_TIMESTAMP;
    IF {{時刻による判定}} THEN
        {{やりたいこと}};
    END IF;
END |
DELIMITER ;

解説

MySQLのスケジューラ設定 CREATE EVENT ON SCHEDULE は以下のパターンでしか設定できない。

  1. 特定の時間に単発実行 (AT [timestamp値])
  2. 一定の間隔で定期実行 (EVERY N [DAY|HOUR等])
    • STARTS [timestamp値] と組み合わせて特定時刻の定期実行も可

dev.mysql.com

午前9時から18時までの毎時0分に特定のストアドプロシージャを呼び出すみたいなケースを考える。

まずスケジュールの記載。範囲指定はできないので毎時実行とする。

CREATE EVENT call_hogehoge_everyday_9to18
ON SCHEDULE EVERY 1 HOUR STARTS '2022-01-01 09:00:00'
DO call hogehoge();

これだけだと毎時実行になってしまうので時刻判定ロジックを書いていくが、、、

  1. 現在時刻を任意の変数に入れて
  2. No.1の変数を時刻比較して範囲内であることをチェックする
  3. trueな場合に任意の処理を実行

としたいためイベント内で複数の処理を呼び出すことになるが、その場合は複合ステートメントを利用する。

dev.mysql.com

ただし複数の処理を呼び出すために途中で ; を入れてしまうとそこでクエリが切れてしまうため、 イベントを作成する前に DELIMITER でデリミタを変更しイベント作成後に戻す必要がある。

DELIMITER |

CREATE EVENT call_hogehoge_everyday_9to18
ON SCHEDULE EVERY 1 HOUR STARTS '2022-01-01 09:00:00'
DO
BEGIN
    call hogehoge();
END |

DELIMITER ;

あとはここにIF文で時間帯判定を追加すれば良い。これで完成形。

DELIMITER |

CREATE EVENT call_hogehoge_everyday_9to18
ON SCHEDULE EVERY 1 HOUR STARTS '2022-01-01 09:00:00'
DO
BEGIN
    SET @NOW = CURRENT_TIMESTAMP;
    IF time(@NOW) NOT BETWEEN hour('09:00:00') AND hour('18:00:00') THEN
        call hogehoge();
    END IF;
END |

DELIMITER ;

平日だけやりたいならIF文の中を dayofweek(@NOW) IN (1,2,3,4,5) とか書けばできる。