概要
MySQLでは、データ型間の変換が頻繁に発生する。
型変換には、暗黙的な変換と明示的な変換の2種類が存在する。
暗黙的な型変換は、演算や比較の際にMySQLが自動的に実行する変換である。
明示的な型変換は、CAST 関数や CONVERT 関数を使用してユーザが指定する変換である。
暗黙的な型変換は、便利である一方で、予期しない結果やパフォーマンスの低下を引き起こす場合がある。
特に、WHERE句でカラムに対して型変換が適用されると、インデックスが使用されなくなる可能性が高い。
MySQL 8.0.17以降では、CAST 関数 および CONVERT 関数で使用可能な型が拡張されている。
FLOAT 型、DOUBLE 型への変換がサポートされ、精度指定も可能になった。
また、MySQL 8.0.22以降では、YEAR 型への変換、および AT TIME ZONE 演算子が追加された。
型変換の動作を正確に理解することは、データの整合性を保ち、パフォーマンスを最適化する上で重要である。
暗黙的な型変換
暗黙的な型変換は、MySQLが自動的に実行する型変換である。
演算子や関数が異なる型の引数を受け取った場合、MySQLは自動的に型を変換して処理を行う。
暗黙的変換が発生する場面
暗黙的な型変換は、以下に示す場面で発生する。
| 場面 | 説明 |
|---|---|
| 比較演算子を使用する場合 | =、!=、<、>、<=、>= 等
|
| 算術演算子を使用する場合 | +、-、*、/、% 等
|
| 関数の引数として使用する場合 | CONCAT 関数、SUBSTRING 関数等
|
| INSERT文またはUPDATE文での値代入 | カラムの型と異なる型の値を挿入または更新する場合 |
| WHERE句での条件評価 | カラムと異なる型の値で比較する場合 |
暗黙的変換の例を以下に示す。
暗黙的変換は、コードの記述を簡潔にする一方で、予期しない動作を引き起こす可能性がある。
SELECT '100' + 50;
-- 結果: 150 (文字列 '100' が数値 100 に変換される)
SELECT CONCAT(123, ' items');
-- 結果: '123 items' (数値 123 が文字列 '123' に変換される)
SELECT * FROM users WHERE user_id = '42';
-- user_idが整数型の場合、文字列 '42' が数値 42 に変換される
文字列から数値への暗黙変換
文字列が数値として解釈される場合、MySQLは文字列の先頭から数値として読み取れる部分までを変換する。
変換ルールを以下に示す。
| 条件 | 説明 |
|---|---|
| 先頭が数字の場合 | 数値として読み取れる部分まで変換 |
| 先頭が数字でない場合 | 0に変換 |
| 指数表記 (科学的表記法) をサポート | 1.5e2 は 150 に変換
|
| カンマ区切りは数値として認識されない | 1,234,567 は 1 に変換 (カンマで終了)
|
文字列から数値への変換例を以下に示す。
このルールにより、予期しない結果が生じる可能性がある。
例えば、'1,234,567' という文字列は、1に変換される。
SELECT '123' + 0;
-- 結果: 123
SELECT '123abc' + 0;
-- 結果: 123 (先頭の数値部分のみ変換)
SELECT 'abc123' + 0;
-- 結果: 0 (先頭が数字でないため0に変換)
SELECT '1.5e2' + 0;
-- 結果: 150 (指数表記を解釈)
SELECT '1,234,567' + 0;
-- 結果: 1 (カンマで数値部分が終了)
SELECT ' 456 ' + 0;
-- 結果: 456 (前後の空白は無視)
数値から文字列への暗黙変換
数値が文字列として解釈される場面では、MySQLは数値を文字列に自動変換する。
数値から文字列への変換は、比較的直感的である。
文字列コンテキストの例を以下に示す。
SELECT CONCAT(2, ' items');
-- 結果: '2 items' (数値 2 が文字列 '2' に変換)
SELECT CONCAT(123.45, ' dollars');
-- 結果: '123.45 dollars'
SELECT CONCAT(NULL, ' test');
-- 結果: NULL
日付型の暗黙変換
MySQLは、日付型の文字列表現を"緩やかな"フォーマットで受け入れる。
日付型の暗黙変換の特徴を以下に示す。
| 規則 | 説明 |
|---|---|
| 区切り文字の柔軟性 | -、/、# 等の区切り文字を使用可能
|
| 区切り文字なしの形式 | 20240815 のような形式も認識
|
| 時刻部分のオプション | 日付のみ、または日付と時刻の両方を指定可能 |
日付型の暗黙変換の例を以下に示す。
ただし、日本語の日付表記 (例: 2024年8月15日) は認識されない。
このような形式の日付を変換する場合は、STR_TO_DATE 関数を使用する。
SELECT CAST('2024-08-15' AS DATE);
-- 結果: 2024-08-15
SELECT CAST('2024/08/15' AS DATE);
-- 結果: 2024-08-15 (区切り文字 / を認識)
SELECT CAST('2024#08#15' AS DATE);
-- 結果: 2024-08-15 (区切り文字 # を認識)
SELECT CAST('20240815' AS DATE);
-- 結果: 2024-08-15 (区切り文字なしも認識)
SELECT CAST('2024-08-15 14:30:00' AS DATETIME);
-- 結果: 2024-08-15 14:30:00
比較時の型変換ルール
MySQLは、異なる型の値を比較する際に、一定のルールに従って型変換を実行する。
型変換の優先順位
MySQLは、以下に示す優先順位で型変換を実行する。
| 優先順位 | 条件 | 変換規則 |
|---|---|---|
| 1 | NULLの場合 | 結果は NULL (<=> 演算子は例外)
|
| 2 | 両方が文字列の場合 | 文字列として比較 |
| 3 | 両方が整数の場合 | 整数として比較 |
| 4 | DECIMALが含まれる場合 | DECIMAL精度ルールに従って比較 |
| 5 | その他の場合 | 浮動小数点数として比較 |
比較時の型変換の例を以下に示す。
SELECT 1 = '1';
-- 結果: 1 (true) (文字列 '1' が数値 1 に変換)
SELECT 0 = 'abc';
-- 結果: 1 (true) (文字列 'abc' が数値 0 に変換)
SELECT 1 = 1.0;
-- 結果: 1 (true) (整数と浮動小数点の比較)
SELECT 'abc' = 'ABC';
-- 結果: 1 (true) (デフォルトの照合順序は大文字小文字を区別しない)
文字列と数値の比較
文字列と数値を比較する場合、文字列が数値に変換 される。
文字列と数値の比較例を以下に示す。
この動作により、予期しない比較結果が生じる可能性がある。
特に、'abc' = 0 が true となる点に注意すること。
SELECT 1 > '6x';
-- 結果: 0 (false) ('6x' は 6 に変換、1 > 6 は false)
SELECT 7 > '6x';
-- 結果: 1 (true) ('6x' は 6 に変換、7 > 6 は true)
SELECT 0 = 'x6';
-- 結果: 1 (true) ('x6' は 0 に変換、0 = 0 は true)
SELECT 'abc' = 0;
-- 結果: 1 (true) ('abc' は 0 に変換)
SELECT '123' > 99;
-- 結果: 1 (true) ('123' は 123 に変換、123 > 99 は true)
照合順序と比較
文字列同士の比較では、照合順序 (Collation) が比較結果に影響する。
大文字小文字を区別する比較を行う場合は、BINARY 演算子 または COLLATE utf8mb4_bin を使用する。
照合順序の影響を以下に示す。
SELECT 'abc' = 'ABC';
-- 結果: 1 (true) (デフォルトの照合順序は大文字小文字を区別しない)
SELECT 'abc' = 'ABC' COLLATE utf8mb4_bin;
-- 結果: 0 (false) (バイナリ比較では区別)
SELECT BINARY 'abc' = 'ABC';
-- 結果: 0 (false) (BINARY演算子でバイナリ比較を強制)
明示的な型変換
明示的な型変換は、CAST 関数 または CONVERT 関数を使用して実行する。
これらの関数により、ユーザが意図した型変換を明確に指定できる。
CAST関数
CAST 関数は、SQL標準の型変換関数である。
基本構文を以下に示す。
CAST(expr AS type [ARRAY])
| 引数 | 説明 |
|---|---|
expr |
変換する値または式 |
type |
変換先の型 |
ARRAY |
JSON配列への変換 (オプション、MySQL 8.0.17以降) |
CAST 関数の使用例を以下に示す。
SELECT CAST('123' AS SIGNED);
-- 結果: 123 (文字列を符号付き整数に変換)
SELECT CAST(123.456 AS DECIMAL(5,2));
-- 結果: 123.46 (浮動小数点数を固定小数点数に変換、四捨五入)
SELECT CAST('2024-08-15' AS DATE);
-- 結果: 2024-08-15 (文字列を日付型に変換)
SELECT CAST('2024-08-15 14:30:00' AS DATETIME);
-- 結果: 2024-08-15 14:30:00 (文字列を日時型に変換)
SELECT CAST(123 AS CHAR);
-- 結果: '123' (数値を文字列に変換)
CONVERT関数
CONVERT 関数は、MySQL独自の型変換関数である。
CONVERT 関数には、2つの構文が存在する。
基本構文を以下に示す。
CONVERT(expr, type)
CONVERT(expr USING transcoding_name)
| 構文 | 説明 |
|---|---|
CONVERT(expr, type) |
CAST関数と同様の型変換
|
CONVERT(expr USING transcoding_name) |
文字セット変換 |
CONVERT 関数の使用例を以下に示す。
SELECT CONVERT('123', SIGNED);
-- 結果: 123
SELECT CONVERT(123.456, DECIMAL(5,2));
-- 結果: 123.46
SELECT CONVERT('Hello' USING utf8mb4);
-- 結果: 'Hello' (文字セットをutf8mb4に変換)
SELECT CONVERT('データ' USING latin1);
-- エラーまたは文字化け (latin1は日本語をサポートしない)
文字セット指定付き CAST 関数の構文を以下に示す。
CAST(expr AS CHAR CHARACTER SET charset_name)
文字セット指定の例を以下に示す。
SELECT CAST('Hello' AS CHAR CHARACTER SET utf8mb4);
-- 結果: 'Hello'
SELECT CAST('データベース' AS CHAR CHARACTER SET utf8mb4);
-- 結果: 'データベース'
CAST / CONVERTで使用可能な型の詳細
CAST 関数 および CONVERT 関数では、以下に示す型への変換がサポートされている。
| 型 | 説明 | 追加されたバージョン |
|---|---|---|
BINARY[(N)] |
バイナリ文字列型 | |
CHAR[(N)] |
固定長文字列型 | |
DATE |
日付型 | |
DATETIME |
日時型 | |
TIME |
時刻型 | |
DECIMAL[(M[,D])] |
固定小数点型 | |
SIGNED [INTEGER] |
符号付き整数型 | |
UNSIGNED [INTEGER] |
符号なし整数型 | |
FLOAT[(p)] |
単精度浮動小数点型 | MySQL 8.0.17以降 |
DOUBLE |
倍精度浮動小数点型 | MySQL 8.0.17以降 |
REAL |
REAL型 (FLOATまたはDOUBLE) | MySQL 8.0.17以降 |
JSON |
JSON型 | |
YEAR |
年型 | MySQL 8.0.22以降 |
| 空間型 | Point、LineString等 | MySQL 8.0.24以降 |
BINARY / CHAR
BINARY 型 および CHAR 型への変換は、文字列またはバイナリ文字列を生成する。
BINARY[(N)] の場合、N はバイト数を指定する。
CHAR[(N)] の場合、N は文字数ではなくバイト数を指定する点に注意すること。
SELECT CAST(123 AS CHAR);
-- 結果: '123'
SELECT CAST('Hello' AS BINARY(10));
-- 結果: 'Hello\0\0\0\0\0' (10バイトに拡張、NULLバイトでパディング)
SELECT CAST('Hello World' AS CHAR(5));
-- 結果: 'Hello' (5バイトに切り詰め)
数値型
数値型への変換には、整数型、固定小数点型、浮動小数点型が含まれる。
| 型 | 説明 |
|---|---|
SIGNED [INTEGER] |
符号付き整数型 (-9223372036854775808 ~ 9223372036854775807) |
UNSIGNED [INTEGER] |
符号なし整数型 (0 ~ 18446744073709551615) |
DECIMAL[(M[,D])] |
固定小数点型 (M: 精度、D: 小数点以下桁数) |
FLOAT[(p)] |
単精度浮動小数点型 (p: 精度 0-53) |
DOUBLE |
倍精度浮動小数点型 |
REAL |
REAL型 (REAL_AS_FLOATモード設定時はFLOAT、それ以外はDOUBLE) |
FLOAT[(p)] の精度指定について以下に示す。
精度 p の範囲 |
変換先 |
|---|---|
| 0〜24 | FLOAT 型 (単精度浮動小数点)
|
| 25〜53 | DOUBLE 型 (倍精度浮動小数点)
|
数値型への変換の例を以下に示す。
SELECT CAST('123' AS SIGNED);
-- 結果: 123
SELECT CAST('-456' AS SIGNED);
-- 結果: -456
SELECT CAST('789' AS UNSIGNED);
-- 結果: 789
SELECT CAST('-123' AS UNSIGNED);
-- 結果: 18446744073709551493 (オーバーフロー)
SELECT CAST(123.456 AS DECIMAL(5,2));
-- 結果: 123.46 (四捨五入)
SELECT CAST('123.456' AS FLOAT);
-- 結果: 123.456
SELECT CAST('123.456' AS DOUBLE);
-- 結果: 123.456
SELECT CAST('1.23e2' AS DOUBLE);
-- 結果: 123 (指数表記を解釈)
日付・時刻型
日付型および時刻型への変換には、以下に示す型が含まれる。
| 型 | 形式 | 説明 |
|---|---|---|
DATE |
YYYY-MM-DD | 日付型 |
TIME |
HH:MM:SS | 時刻型 |
DATETIME |
YYYY-MM-DD HH:MM:SS | 日時型 |
YEAR |
YYYY | 年型 (MySQL 8.0.22以降) |
日付・時刻型への変換の例を以下に示す。
SELECT CAST('2024-08-15' AS DATE);
-- 結果: 2024-08-15
SELECT CAST('14:30:00' AS TIME);
-- 結果: 14:30:00
SELECT CAST('2024-08-15 14:30:00' AS DATETIME);
-- 結果: 2024-08-15 14:30:00
SELECT CAST('2024' AS YEAR);
-- 結果: 2024 (MySQL 8.0.22以降)
SELECT CAST('2024-08-15 14:30:00' AS DATE);
-- 結果: 2024-08-15 (時刻部分は切り捨て)
SELECT CAST('2024-08-15 14:30:00' AS TIME);
-- 結果: 14:30:00 (日付部分は切り捨て)
AT TIME ZONE 演算子による時刻変換 (MySQL 8.0.22以降) を以下に示す。
SELECT CAST('2024-08-15 14:30:00' AS DATETIME) AT TIME ZONE '+00:00';
-- 結果: UTCタイムゾーンに変換
JSON
JSON 型への変換は、文字列、数値、オブジェクト、配列をJSON形式に変換する。
SELECT CAST('{}' AS JSON);
-- 結果: {} (空のJSONオブジェクト)
SELECT CAST('[1,2,3]' AS JSON);
-- 結果: [1,2,3] (JSON配列)
SELECT CAST(123 AS JSON);
-- 結果: 123 (JSON数値)
SELECT CAST('"Hello"' AS JSON);
-- 結果: "Hello" (JSON文字列)
SELECT CAST('{"name":"Alice","age":30}' AS JSON);
-- 結果: {"name":"Alice","age":30} (JSONオブジェクト)
JSON関連の型変換
MySQL 8.0では、JSON型と他の型の間での変換が強化されている。
JSON型への変換
JSON型への変換は、CAST 関数を使用して実行する。
SELECT CAST('{"name":"Alice"}' AS JSON);
-- 結果: {"name":"Alice"}
SELECT CAST('[1,2,3,4,5]' AS JSON);
-- 結果: [1,2,3,4,5]
SELECT CAST(123 AS JSON);
-- 結果: 123
SELECT CAST(true AS JSON);
-- 結果: true
SELECT CAST(NULL AS JSON);
-- 結果: null (JSONのnull値)
JSON型からの変換
JSON型から他の型への変換には、JSON_UNQUOTE 関数 と JSON_EXTRACT 関数を組み合わせる方法がある。
SET @json = '{"name":"Alice","age":30}';
SELECT JSON_EXTRACT(@json, '$.name');
-- 結果: "Alice" (JSON文字列、ダブルクォート付き)
SELECT JSON_UNQUOTE(JSON_EXTRACT(@json, '$.name'));
-- 結果: Alice (ダブルクォートを除去)
SELECT CAST(JSON_EXTRACT(@json, '$.age') AS SIGNED);
-- 結果: 30 (数値に変換)
MySQL 8.0.21以降では、JSON_VALUE 関数が利用可能である。
JSON_VALUE 関数の構文を以下に示す。
JSON_VALUE(json_doc, path [RETURNING type] [on_empty] [on_error])
| 引数 | 説明 |
|---|---|
json_doc |
JSON文書 |
path |
JSONパス式 |
RETURNING type |
戻り型 (オプション、デフォルト: VARCHAR(512)) |
on_empty |
値が存在しない場合の動作 |
on_error |
エラー発生時の動作 |
JSON_VALUE 関数で使用可能な型を以下に示す。
| 分類 | 型 |
|---|---|
| 数値型 | FLOAT、DOUBLE、DECIMAL、SIGNED、UNSIGNED
|
| 日付・時刻型 | DATE、TIME、DATETIME、YEAR
|
| 文字列型 | CHAR
|
| その他 | JSON
|
JSON_VALUE 関数の使用例を以下に示す。
SET @json = '{"name":"Alice","age":30,"salary":50000.50}';
SELECT JSON_VALUE(@json, '$.name');
-- 結果: 'Alice' (デフォルトはVARCHAR(512))
SELECT JSON_VALUE(@json, '$.age' RETURNING SIGNED);
-- 結果: 30 (符号付き整数に変換)
SELECT JSON_VALUE(@json, '$.salary' RETURNING DECIMAL(10,2));
-- 結果: 50000.50 (固定小数点数に変換)
SELECT JSON_VALUE(@json, '$.unknown' RETURNING SIGNED NULL ON EMPTY);
-- 結果: NULL (値が存在しない場合はNULLを返す)
SELECT JSON_VALUE(@json, '$.unknown' RETURNING SIGNED DEFAULT 0 ON EMPTY);
-- 結果: 0 (値が存在しない場合は0を返す)
SELECT JSON_VALUE(@json, '$.invalid' RETURNING SIGNED ERROR ON ERROR);
-- エラー発生 (変換エラー時にエラーを返す)
ON EMPTY 句および ON ERROR 句の動作を以下に示す。
| 句 | 説明 |
|---|---|
NULL ON EMPTY |
値が存在しない場合、NULLを返す (デフォルト) |
DEFAULT value ON EMPTY |
値が存在しない場合、指定したデフォルト値を返す |
ERROR ON EMPTY |
値が存在しない場合、エラーを発生 |
NULL ON ERROR |
変換エラー時、NULLを返す (デフォルト) |
DEFAULT value ON ERROR |
変換エラー時、指定したデフォルト値を返す |
ERROR ON ERROR |
変換エラー時、エラーを発生 |
※注意
ON EMPTY 句は、ON ERROR 句より前に置く必要がある。
JSONと他の型の比較
JSON型と他の型を比較する場合、MySQLは自動的に型変換を実行する。
SET @json = '{"value":123}';
SELECT JSON_EXTRACT(@json, '$.value') = 123;
-- 結果: 1 (true) (JSON数値と整数を比較)
SELECT JSON_EXTRACT(@json, '$.value') = '123';
-- 結果: 1 (true) (JSON数値と文字列を比較)
型変換に関する注意点
データ損失のリスク
型変換により、データが損失または変更される場合がある。
データ損失が発生する例を以下に示す。
- 固定小数点数から整数への変換
- 小数部が切り捨てられる
SELECT CAST(123.456 AS SIGNED); -- 結果: 123 (小数部 .456 が切り捨て)
- 浮動小数点数から固定小数点数への変換
- 丸め誤差が発生する可能性
SELECT CAST(123.456789 AS DECIMAL(5,2)); -- 結果: 123.46 (四捨五入)
- 文字列から整数への変換
- 数値以外の文字が喪失
SELECT CAST('123abc' AS SIGNED); -- 結果: 123 ('abc' が喪失) SELECT CAST('abc123' AS SIGNED); -- 結果: 0 (全ての文字が喪失)
- 日時型から日付型への変換
- 時刻部分が切り捨てられる
SELECT CAST('2024-08-15 14:30:00' AS DATE); -- 結果: 2024-08-15 (時刻部分 14:30:00 が切り捨て)
インデックスへの影響
WHERE句でカラムに対して型変換を適用すると、インデックスが使用されなくなる。
これにより、クエリのパフォーマンスが大幅に低下する可能性がある。
- 良い例
-- 値側で型変換を行う SELECT * FROM users WHERE user_id = 123; -- インデックスが使用される -- または、CASTを使わずに比較 SELECT * FROM users WHERE user_id = CAST('123' AS SIGNED); -- インデックスが使用される
- 悪い例
-- user_idが整数型の場合 SELECT * FROM users WHERE CAST(user_id AS CHAR) = '123'; -- インデックスが使用されない
カラムに対して関数または型変換を適用しないことが、インデックスを有効活用するための基本原則である。
NULLの扱い
CAST 関数 および CONVERT 関数は、NULL を変換すると NULL を返す。
SELECT CAST(NULL AS SIGNED);
-- 結果: NULL
SELECT CAST(NULL AS CHAR);
-- 結果: NULL
SELECT CAST(NULL AS DATE);
-- 結果: NULL
NULL を別の値に置き換える場合は、COALESCE 関数 または IFNULL 関数を使用する。
SELECT COALESCE(CAST(NULL AS SIGNED), 0);
-- 結果: 0
SELECT IFNULL(CAST(NULL AS SIGNED), -1);
-- 結果: -1
文字列と数値の比較でインデックスが使用されない
-- user_idが整数型インデックスの場合
SELECT * FROM users WHERE user_id = '42';
-- 文字列 '42' が数値 42 に変換されるが、インデックスは使用される
-- しかし、以下はインデックスが使用されない
SELECT * FROM users WHERE CAST(user_id AS CHAR) = '42';
カンマ区切り数値の変換
SELECT '1,234,567' + 0;
-- 結果: 1 (カンマで数値部分が終了)
-- 正しく変換するには、カンマを除去する
SELECT CAST(REPLACE('1,234,567', ',', '') AS SIGNED);
-- 結果: 1234567
浮動小数点数の比較
SELECT 0.1 + 0.2 = 0.3;
-- 結果: 0 (false) (浮動小数点誤差により不一致)
SELECT ABS((0.1 + 0.2) - 0.3) < 0.0001;
-- 結果: 1 (true) (誤差を考慮した比較)
-- または、DECIMAL型を使用
SELECT CAST(0.1 AS DECIMAL(10,2)) + CAST(0.2 AS DECIMAL(10,2)) = CAST(0.3 AS DECIMAL(10,2));
-- 結果: 1 (true)
日本語日付のCAST不可
SELECT CAST('2024年8月15日' AS DATE);
-- 結果: NULL (日本語日付は認識されない)
-- STR_TO_DATE関数を使用
SELECT STR_TO_DATE('2024年8月15日', '%Y年%m月%d日');
-- 結果: 2024-08-15
大きな整数と浮動小数点の比較
SELECT 9223372036854775807 = 9223372036854775808.0;
-- 結果: 1 (true) (浮動小数点の精度制限により誤った結果)
-- 整数同士で比較する
SELECT 9223372036854775807 = CAST(9223372036854775808.0 AS SIGNED);
-- 結果: 0 (false) (ただし、オーバーフローに注意)