はじめに
本記事では先日リリースされたプログラミングゲーム「電脳少女プログラミング2088 ─壊レタ君を再構築─」のSQLスキルチェックステージ「打ち捨てられた図書館」について触れていきます。
ゲームの詳細は下記をご覧ください。
前提
筆者が普段使用するRDBMSはほとんどがOracle Databaseです。もう10年以上お付き合いしているデータベースなので、どっぷりとOracle色に染まっています。
MySQLも今やOracleの持ち物ですが、最後に触ったのは7〜8年前で、今回久々に触ることになりました。
ステージのポイント
3つのテーブルをJOINする点と、下記抽出条件を実装する点が、「打ち捨てられた図書館」のポイントになります。
log (ログ) の log.created_at (ログ.作成日) が 2085-08-01 〜 2087-10-20 の期間の memory.id (記憶.ID), memory.talk (記憶.会話内容), battle.result (戦闘.結果) を出力してください。
※問題文より一部抜粋
ER図では、ログ.作成日はdate型であることが示されています。
その他、前提となる条件に関しては特段触れられていません。
私の解答
私が解答したSQLは以下の通りです。
SELECT
memory.id,
memory.talk,
battle.result
FROM
log
JOIN
memory
ON log.memory_id = memory.id
JOIN
battle
ON log.battle_id = battle.id
WHERE log.created_at BETWEEN STR_TO_DATE('2085-08-01', '%Y-%m-%d')
AND STR_TO_DATE('2087-10-20 23:59:59', '%Y-%m-%d %H:%i:%s');
褒めてもらえました。
重箱の隅をつつくような、細部まで目を光らせて、もはや言いがかりでは?と思えるほどの指摘を挙げてきて、メタクソに罵られることを期待して性格をドS系にしたのに、 レイミさんが優しすぎる。
その他の正解
他の方はどのような解答をしているのか見てみたら、どうやら以下のSQLでも正解となる様子。
SELECT
memory.id,
-- 中略 --
FROM
log
-- 中略 --
WHERE
log.created_at BETWEEN '2085-08-01' AND '2087-10-20';
WHERE
句の条件が、私の書き方と異なっています。
「えっ!?これ正解扱いでいいの!?」と思い、実際に試してみたら本当に正解になりました。
この時のレイミさん(ドS)の反応。
手堅くないSQLってどんな構成なんだろう?
個人的に引っかかったポイント
「電脳少女プログラミング2088 ─壊レタ君を再構築─」ではMySQLの文法でSQLを書く必要があります。
なので、私が最初に解答したSQLではSTR_TO_DATE
関数を噛ませて、データ型に対する暗黙変換が行われないようにしています。この関数はOracle Databaseでは使えませんが、そちらではTO_DATE
関数が類似する関数として用意されています。
その点を踏まえつつ、MySQLではDATE型でどのような値を扱うかが大きなポイントになります。
MySQLでカラムに設定できる、日付に関する型の種類
- DATE
- TIME
- DATETIME
- TIMESTAMP
- YEAR
さらに、DATE型に関しては次のように値を持つことが定められています。
DATE 型は、日付部分を含むが時間部分は含まない値に使用されます。
私の解答、冗長ですよね
以上を踏まえると、私の解答したSQLの一部が、冗長であることがわかります。
具体的にはSTR_TO_DATE('2087-10-20 23:59:59', '%Y-%m-%d %H:%i:%s')
の部分。
比較対象がDATE型のカラムなのだから、あえて23時59分59秒を指定する必要性は全くありません。私がいつも使っているOracle DatabaseではDATE型に時分秒を持つので、当たり前のように書いてしまった箇所が、実はMySQLだと不要であることがわかりました。
指摘して欲しかった
私の解答も文法的には間違っていなかったものの、MySQLのクエリとして最適ではありませんでした。確かに構文上はエラーにならないし、自動テストで拾えないタイプの不具合です。
ですけど、こういった「正しい結果を得られるが、エレガントではない」SQLこそレイミさんからフィードバックしてもらえると、より勉強になるだろうと思いました。
特に、今回はたまたまQiita内イベントで他の記事に目を通す機会があったから気付けただけに、もしイベントに参加していなかったら、私はこの誤りに気づくきっかけさえ得られなかった可能性が高いです。
もう一点、2例目のクエリでは暗黙の型変換を行っています。こちらも大抵の場合きちんと動いてくれるのですが、私のようなおじさん世代は「DBに暗黙の型変換をやらせるな」みたいなSQL開発標準が定められているプロジェクトを多々見てきたので、この点も何らかの示唆をレイミさんからフィードバックしてもらえたら良いな、と思いました。
よくありがちな日付書式RR-MM-DD
が悪さを働くことがありますので。
まとめ
日付型の扱いはRDBMSも勿論の事、プログラミング言語ごとにかなり個性が出る傾向を感じます。
初めて、あるいは久しぶりに触れる、プログラミング言語であったりデータベースでは、必ず日付型の扱いに目を通しておくようにしましょう。