【MySQL】MySQL 8.0.17以降に非推奨となったDECIMAL型×UNSIGNEDの代替方法
この記事について
- MySQLにおいて、金銭データを扱う場合など、正確な精度を保持することが重要な場合に
DECIMAL
型を使用します。さらにデータ型の後にUNSIGNED
を付けると 0 と正の数しか格納できなくなります。これらを活用することにより、不正な値を格納できないようにするわけです。しかし、MySQL 8.0.17 以降、この組み合わせは非推奨となりました。そのため、代替案が必要です。この記事ではそちらについて紹介していきます。
先に結論を1つ
- Check制約を活用しましょう!
MySQLにおける DECIMAL型とUNSIGNEDについて
固定小数点型 (真数値) - DECIMAL、NUMERIC
DECIMAL および NUMERIC 型は真数値データ値を格納します。 これらの型は、金銭データを扱う場合など、正確な精度を保持することが重要な場合に使用されます。 MySQL では、NUMERIC は DECIMAL として実装されるので、DECIMAL に関する次の注意事項が NUMERIC にも同様に適用されます。
DECIMAL型の使い方
DECIMAL(X,Y)
- Xには整数部分の桁数を定義します。Yには小数部分の桁数を定義します。よって、
DECIMAL(5,2)
と記載した場合、整数部分5桁まで、小数部分2桁までの値であれば格納可能と言うことになります。それに反する値を挿入しようものならば、エラーが出力されます。
INSERT INTO total_money (id, price) VALUES (1, 1000000);
Error synchronizing data with database
[1264] [22001]: Data truncation: Out of range value for column 'price' at row 1
数値データ型の構文
-
UNSIGNED が指定されている場合、負の値は許可されません。
-
UNSIGNED
をカラムに設定することで、負の値を使わないようにできます。それだけでなく、負の値を使わない分、正の値で扱える範囲の幅を広げることが出来ます。
例えば、tinyint型の場合は
TINYINT[(M)] [UNSIGNED] [ZEROFILL] 普通サイズの整数。 符号付きの範囲は -128 から 127 です。 符号なしの範囲は 0 から 255 です。
-
このDECIMAL
とUNSIGNED
を合わせることにより、正の値を使いつつ、桁数の指定もできるようになっていました。MySQL 8.0.16までは。。。
MySQL 8.0.17以降の対策
MySQL 8.0.17 では、FLOAT、DOUBLE および DECIMAL(およびすべてのシノニム) タイプのカラムに対して UNSIGNED 属性は非推奨になりました。将来のバージョンの MySQL ではサポートされなくなる予定です。
このため、対策を考える必要があります。そこで使うのがMySQL 8.0.16で追加されたCHECK制約です。
CHECK制約に関する記事は以下でも触れているので、確認してみてください。
DECIMAL型とCHECK制約の組み合わせ
- CHECK制約を使いつつ、負の値をカラムに挿入されないようにするには以下のようなクエリを実行しましょう。
-
変更前のテーブル定義
CREATE TABLE total_money ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, price DECIMAL(5, 0) UNSIGNED NOT NULL, PRIMARY KEY (id) );
-
変更後のテーブル定義
CREATE TABLE total_money ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, price DECIMAL(5, 0) NOT NULL, PRIMARY KEY (id), CHECK (price >= 0) );
-
これだけです。
- 既存のカラムに制約をつけたい場合は以下のクエリを実行しましょう。
ALTER TABLE total_money ADD CONSTRAINT CHECK(price >= 0);
これで完璧です
INSERT INTO total_money (id, price) VALUES (1, -1);
Error synchronizing data with database
[3819] [HY000]: Check constraint 'price' is violated.
懸念点
- DECIMAL型とCHECK制約を組み合わせることで、負の数は弾くことはできました。しかし、懸念点もあります。以下のような例です。
-
payment_status
と言うカラムを追加します。
-
CREATE TABLE total_money (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
price DECIMAL(5, 0) NOT NULL,
payment_status TINYINT NOT NULL,
PRIMARY KEY (id),
CHECK (price >= 0),
CHECK (payment_status >= 0)
);
上記の場合、payment_status
に負の値は入りません。payment_status TINYINT NOT NULL UNSIGNED
と同じような制約はついているでしょう。しかし、TINYINT UNSIGNED
の場合は、255まで値が許容されますが、TINYINT CHECK (payment_status >= 0)
では127までしか値が許容されません。以下まとめです。
設定 | 下限値 | 上限値 |
---|---|---|
TINYINTのみ | -128 | 127 |
TINYINT & UNSIGNED | 0 | 255 |
TINYINT & CHECK (payment_status >= 0) | 0 | 127 |
そのため、TINYINT & CHECK制約に変更したことによりエラーになる場合があるので、使い方には注意が必要です。
最後に
- 非推奨になった
UNSIGNED
の代替方法を紹介しました。バージョンアップに伴い、使いやすくなる機能もあれば、制約も出てくるということを学びました。今後もその観点は持って、技術選定やキャッチアップを進めてきたいと思います。
Discussion