概要
SQLで「秒」を hh:mm:ss 形式に変換する処理を書いたときに、
一見正しそうなのに結果がおかしくなるミスをやらかしたので備忘録として残します。
要件
あるテーブルに「経過時間(秒)」が数値で入っており、
それを VIEW 用に hh:mm:ss 形式へ変換する必要がありました。
要件まとめ
- 入力:経過時間(秒)
- 出力形式:
hh:mm:ss - 0埋めする(
00:00:00) - 出力型:
varchar
データ例
変換前
| 列名 | 型 | 値 |
|---|---|---|
| 経過時間 | decimal(10,0) | 43154 |
変換後(期待値)
| 列名 | 型 | 値 |
|---|---|---|
| 経過時間 | varchar | 11:59:14 |
やらかしたSQL
SELECT文の一部を抜粋します。
RIGHT('0' + CAST([経過時間] / 3600 AS VARCHAR), 2) + ':'
+ RIGHT('0' + CAST(([経過時間] % 3600) / 60 AS VARCHAR), 2) + ':'
+ RIGHT('0' + CAST([経過時間] % 60 AS VARCHAR), 2)
実行結果
22:33:14
🙃
全然違う。
原因
原因は decimal 型のまま割り算・剰余演算をしていたことでした。
-
経過時間の型:decimal(10,0) -
SQL Server では
decimal / intdecimal % int
の計算結果も
decimalになる
その結果、意図しない計算結果になっていました。
つまり、
見た目は整数っぽいが、内部的には
decimalのまま計算されていた
のが原因でした。
正しいSQL
bigint に 明示的にキャストしてから計算することで解決しました。
RIGHT('0' + CAST(CAST([経過時間] AS bigint) / 3600 AS VARCHAR), 2) + ':'
+ RIGHT('0' + CAST((CAST([経過時間] AS bigint) % 3600) / 60 AS VARCHAR), 2) + ':'
+ RIGHT('0' + CAST(CAST([経過時間] AS bigint) % 60 AS VARCHAR), 2) AS hms_time
教訓
- 数値型は見た目だけで判断しない
- decimal のまま計算すると、想定外の結果になることがある
- 割り算や剰余演算は特に注意する
- 意図した計算をしたい場合は、型を明示的にキャストする
追記
- DATEADD関数は、経過時間が24時間以上の秒数には対応できない