【MariaDB】SQLで遊んでみる(日付編)

MariaDB

最後の以外は業務でも役に立つことがあるかも知れませんが、最後のは完全に趣味の世界です。

もしかしたら今後役に立つことがあるかも知れないので記録しておきます。

やること

  • 日付の所属週の月曜日を求める
  • 日付の所属月の月初日を求める
  • 日付の所属月の末日を求める
  • 日付の所属月の日付を全て表示する
  • 日付の所属月のカレンダーを表示する(曜日がカラム)

前提条件

  • MariaDB 10.2以降(11.4.5を使っていきます)

日付の所属週の月曜日を求める

対象日付を「2025/08/17(日)~2025/08/25(月)」として

次の計算ルールを適用してみます。

▼計算ルール:

  • 週の始まりを月曜とする
  • 日付の所属週の月曜を求める

▼対応方針

WEEKDAY()関数が返す数値(月曜が0、日曜が6)を使って、

DATE_SUB()関数で対象日付から曜日分を引き算すればOK。

▼SQL

SELECT
    target_date,
    CASE
        WHEN WEEKDAY(target_date) = 0 THEN '月'
        WHEN WEEKDAY(target_date) = 1 THEN '火'
        WHEN WEEKDAY(target_date) = 2 THEN '水'
        WHEN WEEKDAY(target_date) = 3 THEN '木'
        WHEN WEEKDAY(target_date) = 4 THEN '金'
        WHEN WEEKDAY(target_date) = 5 THEN '土'
        WHEN WEEKDAY(target_date) = 6 THEN '日'
    END AS 'youbi',
    DATE_FORMAT(DATE_SUB(d.target_date, INTERVAL WEEKDAY(d.target_date) DAY), '%Y/%m/%d') AS `monday`
FROM
    (
        SELECT '2025/08/17' AS `target_date`
        UNION ALL SELECT '2025/08/18' AS `target_date`
        UNION ALL SELECT '2025/08/19' AS `target_date`
        UNION ALL SELECT '2025/08/20' AS `target_date`
        UNION ALL SELECT '2025/08/21' AS `target_date`
        UNION ALL SELECT '2025/08/22' AS `target_date`
        UNION ALL SELECT '2025/08/23' AS `target_date`
        UNION ALL SELECT '2025/08/24' AS `target_date`
        UNION ALL SELECT '2025/08/25' AS `target_date`
    ) AS `d`

▼実行結果

+-------------+-------+------------+
| target_date | youbi | monday     |
+-------------+-------+------------+
| 2025/08/17  | 日    | 2025/08/11 |
| 2025/08/18  | 月    | 2025/08/18 |
| 2025/08/19  | 火    | 2025/08/18 |
| 2025/08/20  | 水    | 2025/08/18 |
| 2025/08/21  | 木    | 2025/08/18 |
| 2025/08/22  | 金    | 2025/08/18 |
| 2025/08/23  | 土    | 2025/08/18 |
| 2025/08/24  | 日    | 2025/08/18 |
| 2025/08/25  | 月    | 2025/08/25 |
+-------------+-------+------------+
9 rows in set (0.001 sec)

日付の所属月の月初日を求める

書くまでもないとは思うのですが手順として書いておきます。

▼対応方針

DATE_FORMAT()関数で日の箇所を「01」にするだけですね。

▼SQL

SELECT
    target_date,
    DATE_FORMAT(target_date, '%Y/%m/01') AS `first_day`
FROM
    (
        SELECT '2025/07/31' AS `target_date`
        UNION ALL SELECT '2025/08/23' AS `target_date`
        UNION ALL SELECT '2025/09/01' AS `target_date`
    ) AS `d`

▼実行結果

+-------------+------------+
| target_date | first_day  |
+-------------+------------+
| 2025/07/31  | 2025/07/01 |
| 2025/08/23  | 2025/08/01 |
| 2025/09/01  | 2025/09/01 |
+-------------+------------+
3 rows in set (0.001 sec)

日付の所属月の末日を求める

これも関数一発なので特に書く必要はないと思いますが手順として。

▼対応方針

LAST_DAY()関数を使って対象日付の所属月の末日を求める。

▼SQL

SELECT
    d.target_date,
    LAST_DAY(d.target_date) AS `last_day`
FROM
    (
        SELECT '2025/07/31' AS `target_date`
        UNION ALL SELECT '2025/08/23' AS `target_date`
        UNION ALL SELECT '2025/09/01' AS `target_date`
    ) AS `d`

▼実行結果

+-------------+------------+
| target_date | last_day   |
+-------------+------------+
| 2025/07/31  | 2025-07-31 |
| 2025/08/23  | 2025-08-31 |
| 2025/09/01  | 2025-09-30 |
+-------------+------------+
3 rows in set (0.001 sec)

日付の所属月の日付を全て表示する

特定日付の所属月の日付を全て表示してみます。

▼対応方針

特定日付を変数「target_date」にセット。

WITH RECURSIVE を使って末日まで再帰処理。

▼SQL

SET @target_date = '2025/07/07';

WITH RECURSIVE dates AS (
    SELECT DATE_FORMAT(@target_date, '%Y/%m/01') AS dt
    UNION ALL
    SELECT
        DATE_ADD(dt, INTERVAL 1 DAY)
    FROM
        dates
    WHERE
        dt < LAST_DAY(@target_date)
)
SELECT
    DATE_FORMAT(dt, '%Y/%m/%d') AS `date_of_month`
FROM
    dates
ORDER BY
    date_of_month ASC
;

▼実行結果

+---------------+
| date_of_month |
+---------------+
| 2025/07/01    |
| 2025/07/02    |
| 2025/07/03    |
| 2025/07/04    |
| 2025/07/05    |
| 2025/07/06    |
| 2025/07/07    |
| 2025/07/08    |
| 2025/07/09    |
| 2025/07/10    |
| 2025/07/11    |
| 2025/07/12    |
| 2025/07/13    |
| 2025/07/14    |
| 2025/07/15    |
| 2025/07/16    |
| 2025/07/17    |
| 2025/07/18    |
| 2025/07/19    |
| 2025/07/20    |
| 2025/07/21    |
| 2025/07/22    |
| 2025/07/23    |
| 2025/07/24    |
| 2025/07/25    |
| 2025/07/26    |
| 2025/07/27    |
| 2025/07/28    |
| 2025/07/29    |
| 2025/07/30    |
| 2025/07/31    |
+---------------+
31 rows in set (0.001 sec)

日付の所属月のカレンダーを表示する(曜日がカラム)

これがイマイチ何の役に立つのかわかりません。

もう趣味の世界です。

▼対応方針

  • 対象日付を変数「target_date」に格納
  • 日付の出力書式を変数「date_format」に格納
  • 非表示欄の文字列を変数「blank_day」に格納
  • 曜日をカラムにする
  • 日曜スタートで表示
  • 日付は2桁で右寄せ表示(頭スペース埋め)
  • 各日付の週番号を計算
  • 週番号でGROUP BY
  • 週番号昇順でソート

▼SQL

SET @target_date = '2025/07/07';
SET @date_format = '%e';
SET @blank_day = '-';

WITH RECURSIVE
    dates AS (
        SELECT DATE_FORMAT(@target_date, '%Y/%m/01') AS dt
        UNION ALL
        SELECT
            DATE_ADD(dt, INTERVAL 1 DAY)
        FROM
            dates
        WHERE
            dt < LAST_DAY(@target_date)
    )
SELECT
  LPAD(IFNULL(MIN(CASE WHEN WEEKDAY(dt) = 6 THEN DATE_FORMAT(dt, @date_format) END), @blank_day), 2, ' ') AS '日',
  LPAD(IFNULL(MIN(CASE WHEN WEEKDAY(dt) = 0 THEN DATE_FORMAT(dt, @date_format) END), @blank_day), 2, ' ') AS '月',
  LPAD(IFNULL(MIN(CASE WHEN WEEKDAY(dt) = 1 THEN DATE_FORMAT(dt, @date_format) END), @blank_day), 2, ' ') AS '火',
  LPAD(IFNULL(MIN(CASE WHEN WEEKDAY(dt) = 2 THEN DATE_FORMAT(dt, @date_format) END), @blank_day), 2, ' ') AS '水',
  LPAD(IFNULL(MIN(CASE WHEN WEEKDAY(dt) = 3 THEN DATE_FORMAT(dt, @date_format) END), @blank_day), 2, ' ') AS '木',
  LPAD(IFNULL(MIN(CASE WHEN WEEKDAY(dt) = 4 THEN DATE_FORMAT(dt, @date_format) END), @blank_day), 2, ' ') AS '金',
  LPAD(IFNULL(MIN(CASE WHEN WEEKDAY(dt) = 5 THEN DATE_FORMAT(dt, @date_format) END), @blank_day), 2, ' ') AS '土'
FROM
    dates
GROUP BY
    FLOOR((DAY(dt) + WEEKDAY(DATE_FORMAT(dt, '%Y/%m/01'))) / 7)
ORDER BY
    MIN(dt) ASC
;

▼実行結果

+------+------+------+------+------+------+------+
| 日   | 月   | 火   | 水   | 木   | 金   | 土   |
+------+------+------+------+------+------+------+
|  -   |  -   |  1   |  2   |  3   |  4   |  5   |
|  6   |  7   |  8   |  9   | 10   | 11   | 12   |
| 13   | 14   | 15   | 16   | 17   | 18   | 19   |
| 20   | 21   | 22   | 23   | 24   | 25   | 26   |
| 27   | 28   | 29   | 30   | 31   |  -   |  -   |
+------+------+------+------+------+------+------+
5 rows in set (0.001 sec)

もうアホですね。

  • 1
  • 1
  • 0
  • 0

コメント

タイトルとURLをコピーしました