untitled .engineer

技術系のブログ(仮)

「プログラマのためのSQL第4版」のサンプルコードをMySQLで動くようにしてみた(38.5 その他の時間関数)


目次


f:id:dupont_kedama:20190228135448p:plain

本エントリの概要

  • プログラマのためのSQL第4版」の読書会に参加させてもらってるのですが、たまには予習をしようと思い付きでやってみました。
  • 突発的なやつなので継続はしないつもりです。
  • 本エントリでは動かすことが目的なので内容の理解は後回しです。

第38章 38.5 その他の時間関数 P730

元のSQL

CREATE FUNCTION Zeller (z_year IN INTEGER, z_month IN INTEGER, z_day IN INTEGER)
RETURN INTEGER
IS
    m INTEGER;
    d INTEGER;
    y INTEGER;
BEGIN
    y := z_year;
    m := z_month - 2;
    IF (m <= 0)
    THEN m := m + 12;
    y := y - 1;
    END IF;

    RETURN (MOD((z_day + (13 * m - 1)/5
             + 5 * MOD(y, 100)/4 - 7 * y/400), 7) + 1);
END;

MySQLで動くように変換したSQL

CREATE FUNCTION Zeller
(z_year INTEGER, z_month INTEGER, z_day INTEGER)
RETURNS INTEGER DETERMINISTIC
BEGIN
    DECLARE m INTEGER;
    DECLARE d INTEGER;
    DECLARE y INTEGER;
    SET y = z_year;
    SET m = z_month - 2;
    IF (m <= 0)
    THEN SET m = m + 12;
    SET y = y - 1;
    END IF;

    RETURN (MOD((z_day + (13 * m - 1)/5
             + 5 * MOD(y, 100)/4 - 7 * y/400), 7) + 1);
END;

ポイント

  • 関数のパラメーター z_year IN INTEGER の部分を z_year INTEGER のように書きなおします。
  • RETURN INTEGER としているところを RETURNS INTEGER DETERMINISTIC に書き換えます。
  • IS以降で記載している変数宣言をBEGIN以下に移動します。
  • 変数宣言はそれぞれ DECLARE をつけて DECLARE m INTEGER; のようにます。
  • 変数への値の代入は y := z_year; から SET y = z_year; のように書きなおします。

検証

次回の読書会の日付 2019/02/28(木) を調べてみます。

mysql> SELECT Zeller(28,2,2019);
+-------------------+
| Zeller(28,2,2019) |
+-------------------+
|                 5 |
+-------------------+
1 row in set (0.02 sec)

ツェラーの公式」なるものがあるらしいので検索してみると Wikipedia ツェラーの公式 に、2パターンの式があります。
このうち、木曜日が5になるのは h(ISO 8601ではない方)としている方だということになります。
一応Wikipediaに書いてある計算式で手計算してみたけどhの方で合ってました。

(公式にあるグレゴリオ暦ユリウス暦での分岐は上記関数にはなく、1582年以降を想定しているようです。)

結果

  • 動きました。

検証環境

MySQL8.0.12

PostgreSQLのDOW

書籍本文には

PostgreSQLはDOWという入力の日付に対する曜日を返す関数を持っている。2~8の値を返し、8が日曜日である。

と書かれていますが、なんとなく違和感があったので畑違いですが調べてみました。(そもそも畑持ってないけど)

①EXTRACT または date_part関数
https://www.postgresql.jp/document/10/html/functions-datetime.html

SELECT EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40');
SELECT date_part('dow' FROM TIMESTAMP '2001-02-16 20:38:40');

が返すのは 日曜日(0)から土曜日(6)までの曜日 ・・・これではないですね。

②to_char関数
https://www.postgresql.jp/document/10/html/functions-formatting.html

to_char(timestamp, 'D') または to_char(timestamp, 'ID')

が返すのは 曜日番号、日曜日(1)から土曜日(7)まで (IDはISO 8601のそれ)だそうなのでこれでもないですね。
(そもそもDOWではない)

なんなんでしょう?
(正誤表にはありませんでした)