- SQL
SQL CASTで型変換|文字列・数値・日付の変換をDB別早見表で解説
CASTとは、値のデータ型を別の型に変換する標準SQLの書き方で、CAST(値 AS 型) と書きます。 たとえば CAST('123' AS INTEGER) は文字列の '123' を整数の 123 に変換します。書き方そのものは多くのデータベースで共通ですが、指定できる型の名前、変換できない値を渡したときの挙動、小数を整数にするときの丸め方は、データベースごとに違います。
この記事では、文字列・数値・日付の変換パターンを MySQL・PostgreSQL・SQL Server・Oracle で書き分けた早見表を最初に示します。そのうえで、MySQL 8.0.46 と PostgreSQL 16.15 で実際に実行した結果とエラー文を載せ、SQL Server と Oracle は今回公式ドキュメントを開いて確認した内容を、実行していないことを明記したうえで紹介します。'abc' を数値にしたとき、存在しない日付を渡したとき、桁があふれたときの挙動、丸め方の違い、型変換でインデックスが使われなくなる例と直し方まで扱います。
なお、MySQL 8.0 は2026年4月にサポートが終了しています。手元で動かせた版が 8.0.46 のためその結果を載せています。8.4 以降をお使いの場合は、同じ SQL を手元で流して確認してください。
SQL CAST・型変換の早見表(DB別)
| やりたいこと | MySQL | PostgreSQL | SQL Server | Oracle |
|---|---|---|---|---|
| 文字列→整数 | CAST('123' AS SIGNED) |
CAST('123' AS integer) / '123'::int |
CAST('123' AS int) |
CAST('123' AS NUMBER) / TO_NUMBER('123') |
| 文字列→小数 | CAST('12.345' AS DECIMAL(10,2)) |
CAST('12.345' AS numeric(10,2)) |
CAST('12.345' AS decimal(10,2)) |
TO_NUMBER('12.345') |
| 数値→文字列 | CAST(123 AS CHAR) |
CAST(123 AS varchar) / 123::text |
CAST(123 AS varchar(10)) |
TO_CHAR(123) |
| 数値→書式つき文字列 | FORMAT(1234567.891, 2) |
to_char(1234567.891, 'FM9,999,999.00') |
FORMAT(...) ※ |
TO_CHAR(n, '9,999,999.00') ※ |
| ゼロ埋め | LPAD(42, 5, '0') |
to_char(42, 'FM00000') / lpad(42::text, 5, '0') |
― | ― |
| 文字列→日付(標準の形式) | CAST('2026-10-02' AS DATE) |
CAST('2026-10-02' AS date) / '2026-10-02'::date |
CAST('2026-10-02' AS date) |
TO_DATE('2026-10-02', 'YYYY-MM-DD') |
| 文字列→日付(書式を指定) | STR_TO_DATE('2026/10/02', '%Y/%m/%d') |
to_date('2026/10/02', 'YYYY/MM/DD') |
CONVERT(date, '2026/10/02', 111) |
TO_DATE('2026/10/02', 'YYYY/MM/DD') |
| 日付→文字列(書式を指定) | DATE_FORMAT(d, '%Y/%m/%d %H:%i') |
to_char(d, 'YYYY/MM/DD HH24:MI') |
CONVERT(varchar, d, 120) |
TO_CHAR(d, 'YYYY/MM/DD HH24:MI') |
| 日時→日付 | CAST(dt AS DATE) |
CAST(dt AS date) / dt::date |
CAST(dt AS date) |
TRUNC(dt) ※ |
| 変換できない値を NULL にする | 正規表現で確かめてから CAST | pg_input_is_valid()(16以降) |
TRY_CAST('abc' AS int) |
CAST('abc' AS NUMBER DEFAULT NULL ON CONVERSION ERROR) |
- MySQL と PostgreSQL の列は、この記事のために実行して確かめた書き方です(結果は各節に掲載)。
- SQL Server と Oracle の列は実行していません。CAST・CONVERT・TRY_CAST(SQL Server)、CAST・TO_NUMBER・TO_DATE・TO_CHAR(日時)(Oracle 19c)は、今回公式ドキュメントで構文を確認しました。※を付けた項目は公式ページを開いておらず、一般に知られた書き方として載せています。
| 関数・演算子 | 使えるDB | 役割 |
|---|---|---|
CAST(値 AS 型) |
すべて | 標準SQLの型変換 |
CONVERT(値, 型) |
MySQL | CAST と同じ意味。CONVERT(値 USING 文字コード) は文字コードの変換 |
CONVERT(型, 値, スタイル) |
SQL Server | 引数の順番が MySQL と逆。日付の書式を番号で指定できる |
値::型 |
PostgreSQL | CAST の短い書き方 |
TRY_CAST(値 AS 型) |
SQL Server | 変換できなければ NULL を返す |
TO_CHAR / TO_DATE / TO_NUMBER |
PostgreSQL・Oracle | 書式(フォーマット)を指定して変換 |
DATE_FORMAT / STR_TO_DATE |
MySQL | 書式を指定して日付と文字列を変換 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SQL CASTで型変換|文字列・数値・日付の変換をDB別早見表で解説」
CAST の基本と CONVERT・:: との違い
CAST(値 AS 型)
CAST は標準SQLで決められた書き方なので、どのデータベースでも通じます。違うのは「AS の後ろに書ける型の名前」です。次の表は、同じ「文字列を整数に」をそれぞれの書き方で実行した結果です。
| DB | 書き方 | 結果 |
|---|---|---|
| MySQL 8.0 | CAST('123' AS SIGNED) |
123 |
| MySQL 8.0 | CONVERT('123', SIGNED) |
123 |
| MySQL 8.0 | '123' + 0(暗黙の変換) |
123 |
| PostgreSQL 16 | CAST('123' AS integer) |
123 |
| PostgreSQL 16 | '123'::int |
123 |
| PostgreSQL 16 | int4 '42'(型名を前に置く書き方) |
42 |
移植しやすさを優先するなら CAST、PostgreSQL だけで使うなら :: が短く書けます。 MySQL と SQL Server の CONVERT は名前が同じでも引数の順番が逆(MySQL は CONVERT(値, 型)、SQL Server は CONVERT(型, 値, スタイル))なので、複数のデータベースを行き来するときは CAST にそろえておくと混乱を避けられます。
MySQL の CAST で使える型(INT や VARCHAR はエラーになる)
MySQL の CAST は、テーブル定義で使う型名をそのまま書けるわけではありません。INT や VARCHAR と書くと構文エラーになります。
SELECT CAST('123' AS INT);
SELECT CAST(123 AS VARCHAR(10));
SELECT CAST('123' AS INTEGER);
MySQL 8.0.46 の実行結果:
ERROR 1064 (42000) at line 5: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INT)' at line 1
ERROR 1064 (42000) at line 6: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'VARCHAR(10))' at line 1
ERROR 1064 (42000) at line 7: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTEGER)' at line 1
整数は SIGNED(または SIGNED INTEGER)か UNSIGNED、文字列は CHAR と書きます。
SELECT CAST('1.5' AS FLOAT) a, CAST('123' AS SIGNED INTEGER) b, CAST(1 AS CHAR(5)) c;
-- 1.5 | 123 | 1
MySQL 8.0 の公式リファレンスで確認した、CAST に指定できる型の一覧です。
| 指定する型 | 変換後 | メモ |
|---|---|---|
SIGNED [INTEGER] |
符号つき BIGINT | 整数に変換するときの基本 |
UNSIGNED [INTEGER] |
符号なし BIGINT | 負の値を渡すと大きな正の数になる(後述) |
DECIMAL[(M[,D])] |
DECIMAL | 省略時は M=10, D=0(小数部なし) |
FLOAT[(p)] / DOUBLE |
浮動小数点 | 8.0.17 で追加 |
REAL |
DOUBLE(設定により FLOAT) | ― |
CHAR[(N)] |
VARCHAR | 文字列に変換するときの基本 |
NCHAR[(N)] |
各国語文字セットの文字列 | ― |
BINARY[(N)] |
VARBINARY | ― |
DATE / DATETIME[(M)] / TIME[(M)] |
日付・日時・時刻 | M は秒の小数部の桁数 |
YEAR |
YEAR | 8.0.22 で追加 |
JSON |
JSON | ― |
| 書きたくなる型 | MySQL の CAST での書き方 |
|---|---|
INT / INTEGER / BIGINT |
SIGNED または UNSIGNED |
VARCHAR(n) / TEXT |
CHAR(n) / CHAR |
NUMERIC(p,s) |
DECIMAL(p,s) |
TIMESTAMP |
DATETIME |
CAST('12.345' AS DECIMAL) のように桁数を省略すると小数部が0桁になり、12 に丸められます。小数を残したいときは DECIMAL(10,2) のように桁数を必ず書きます。 データ型そのものの違いは、別記事「SQLデータ型一覧|MySQL・PostgreSQL比較とVARCHARとTEXT」で扱っています。
文字列 ⇔ 数値の変換
MySQL 8.0 の実行結果
SELECT CAST('123' AS SIGNED) a,
CAST('12.345' AS DECIMAL(10,2)) c,
CAST('1.5e3' AS DOUBLE) d,
CAST(123 AS CHAR) e,
CAST(123.456 AS CHAR) f,
'123' + 0 g,
CONCAT(123, '') h;
| a | c | d | e | f | g | h |
| 123 | 12.35 | 1500 | 123 | 123.456 | 123 | 123 |
SELECT FORMAT(1234567.891, 2) a, LPAD(42, 5, '0') b, CAST(1234567.891 AS DECIMAL(12,1)) c;
-- 1,234,567.89 | 00042 | 1234567.9
PostgreSQL 16 の実行結果
SELECT CAST('123' AS integer) a, '123'::int b,
CAST('12.345' AS numeric(10,2)) c,
'1.5e3'::float8 d,
CAST(123 AS varchar) e, 123.456::text f,
to_number('1,234.5', '9G999D9') g;
a | b | c | d | e | f | g
-----+-----+-------+------+-----+---------+--------
123 | 123 | 12.35 | 1500 | 123 | 123.456 | 1234.5
SELECT to_char(1234567.891, 'FM9,999,999.00') a, to_char(42, 'FM00000') b,
lpad(42::text, 5, '0') c, CAST(1234567.891 AS numeric(12,1)) d;
-- 1,234,567.89 | 00042 | 00042 | 1234567.9
'12.345' を小数2桁に変換すると、どちらも 12.35 に丸められました。カンマ区切りの '1,234.5' は CAST では変換できないので、PostgreSQL では to_number に書式を渡します(G は桁区切り、D は小数点)。to_char の FM は、前後の余分な空白を付けない指定です。
文字列 ⇔ 日付の変換
標準的な形式(YYYY-MM-DD)は CAST で変換できる
-- MySQL 8.0
SELECT CAST('2026-10-02' AS DATE) a,
CAST('2026-10-02 13:45:10' AS DATETIME) b,
CAST('20261002' AS DATE) c,
CAST(TIMESTAMP '2026-10-02 13:45:10' AS DATE) e,
CAST(DATE '2026-10-02' AS DATETIME) f;
| a | b | c | e | f |
| 2026-10-02 | 2026-10-02 13:45:10 | 2026-10-02 | 2026-10-02 | 2026-10-02 00:00:00 |
PostgreSQL 16 でも、型名を date・timestamp に変えた同じクエリで同じ値になりました。日時を日付に CAST すると時刻が切り落とされ、日付を日時に CAST すると 00:00:00 が補われます。
形式が違う文字列は、書式を指定する関数を使う
2026/10/02 や 02-10-2026 のような形式は、書式を指定して読み取ります。
-- MySQL 8.0
SELECT STR_TO_DATE('2026/10/02', '%Y/%m/%d') a,
STR_TO_DATE('02-10-2026 13:45', '%d-%m-%Y %H:%i') b,
DATE_FORMAT(TIMESTAMP '2026-10-02 13:45:10', '%Y/%m/%d %H:%i') c,
DATE_FORMAT(DATE '2026-10-02', '%Y年%c月%e日') d;
| a | b | c | d |
| 2026-10-02 | 2026-10-02 13:45:00 | 2026/10/02 13:45 | 2026年10月2日 |
-- PostgreSQL 16
SELECT to_date('2026/10/02', 'YYYY/MM/DD') a,
to_char(TIMESTAMP '2026-10-02 13:45:10', 'YYYY/MM/DD HH24:MI') c,
to_char(DATE '2026-10-02', 'YYYY"年"FMMM"月"FMDD"日"') d,
to_char(DATE '2026-10-02', 'YYYYMMDD')::int f;
a | c | d | f
------------+------------------+---------------+----------
2026-10-02 | 2026/10/02 13:45 | 2026年10月2日 | 20261002
書式の記号は MySQL と PostgreSQL(Oracle も同じ系統)で異なります。
| 意味 | MySQL(DATE_FORMAT / STR_TO_DATE) |
PostgreSQL(to_char / to_date) |
|---|---|---|
| 年(4桁) | %Y |
YYYY |
| 月(2桁) | %m |
MM |
| 月(ゼロなし) | %c |
FMMM |
| 日(2桁) | %d |
DD |
| 日(ゼロなし) | %e |
FMDD |
| 時(24時間) | %H |
HH24 |
| 分 | %i |
MI |
| 秒 | %s |
SS |
MySQL の「分」は %i、PostgreSQL の「分」は MI です。 %m や MM は「月」なので、分のつもりで書くと月が入ります。日付の加算や差分など、変換以外の日付操作は、別記事「SQL 日付関数 一覧|4DB比較表とコピペSQL・実行結果つき」にまとめています。
日付を数値にする
MySQL では日付を整数に CAST すると YYYYMMDD 形式の数値になりました。PostgreSQL では to_char で文字列にしてから整数に変換します。
SELECT CAST(DATE '2026-10-02' AS UNSIGNED); -- MySQL: 20261002
SELECT to_char(DATE '2026-10-02', 'YYYYMMDD')::int; -- PostgreSQL: 20261002
SQL Server と Oracle の書き方(公式ドキュメントで確認・未実行)
SQL Server の CONVERT は、3番目の引数に「スタイル」という番号を渡して日付の書式を指定します。公式ドキュメントで確認した主な番号は次のとおりです。
| スタイル | 書式 |
|---|---|
| 23 | yyyy-mm-dd |
| 111 | yyyy/mm/dd |
| 112 | yyyymmdd |
| 120 | yyyy-mm-dd hh:mi:ss(24時間) |
| 126 | yyyy-mm-ddThh:mi:ss.mmm(ISO 8601) |
-- SQL Server(未実行。構文は公式ドキュメントで確認)
SELECT CONVERT(varchar(10), GETDATE(), 23); -- 日付→文字列
SELECT CONVERT(date, '2026/10/02', 111); -- 文字列→日付
Oracle は TO_DATE(文字列, 書式) と TO_CHAR(日時, 書式) を使います。公式ドキュメントでは、TO_DATE には書式を必ず指定するよう勧められています(省略すると、セッションの NLS_DATE_FORMAT などの設定に結果が左右されるため)。
-- Oracle(未実行。構文は公式ドキュメントで確認)
SELECT TO_DATE('2026/10/02', 'YYYY/MM/DD') FROM DUAL;
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
変換できない値を渡したときの挙動(PostgreSQL はエラー、MySQL は 0 と警告)
型変換で最も差が出るのがここです。同じ CAST('abc' AS 整数) でも、PostgreSQL はエラーで止まり、MySQL の SELECT は 0 を返して警告を出します。
'abc' を数値に変換する
-- MySQL 8.0
SELECT CAST('abc' AS SIGNED) a;
| a |
| 0 |
Warning (Code 1292): Truncated incorrect INTEGER value: 'abc'
SELECT CAST('12abc' AS SIGNED) a, CAST('' AS SIGNED) b, CAST('abc' AS DECIMAL(5,2)) c, CAST(NULL AS SIGNED) d;
| a | b | c | d |
| 12 | 0 | 0.00 | NULL |
Warning (Code 1292): Truncated incorrect INTEGER value: '12abc'
Warning (Code 1292): Truncated incorrect INTEGER value: ''
Warning (Code 1292): Truncated incorrect DECIMAL value: 'abc'
MySQL は、文字列の先頭から数値として読める部分だけを使います。'12abc' は 12、'abc' と空文字は 0 になりました。警告は mysql コマンドを --show-warnings 付きで起動するか、直後に SHOW WARNINGS; を実行すると表示されます。
-- PostgreSQL 16
SELECT CAST('abc' AS integer);
SELECT CAST('12abc' AS integer);
SELECT CAST('' AS integer);
ERROR: invalid input syntax for type integer: "abc"
ERROR: invalid input syntax for type integer: "12abc"
ERROR: invalid input syntax for type integer: ""
存在しない日付・桁あふれ
| 入力 | MySQL 8.0(SELECT) | PostgreSQL 16 |
|---|---|---|
CAST('abc' AS 整数) |
0 + 警告 1292 | ERROR: invalid input syntax for type integer: "abc" |
CAST('12abc' AS 整数) |
12 + 警告 1292 | エラー(同上) |
CAST('' AS 整数) |
0 + 警告 1292 | エラー(同上) |
CAST(NULL AS 整数) |
NULL | NULL |
CAST('2026-02-30' AS DATE) |
NULL + 警告 Incorrect datetime value: '2026-02-30' |
ERROR: date/time field value out of range: "2026-02-30" |
STR_TO_DATE / to_date に '2026-02-30' |
NULL + 警告 1411 | エラー(同上) |
CAST('99999999999999999999' AS SIGNED / bigint) |
-1 + 警告 1292 | ERROR: value "99999999999999999999" is out of range for type bigint |
CAST(99999999999 AS integer) |
―(SIGNED は BIGINT のため収まる) | ERROR: integer out of range |
CAST(12345.678 AS DECIMAL(5,2)) |
999.99 + 警告 1264 | ERROR: numeric field overflow |
CAST(-1 AS UNSIGNED) |
18446744073709551615 | ―(符号なし整数型がない) |
CAST('abcdef' AS CHAR(3) / varchar(3)) |
abc + 警告 1292 |
abc(エラーなし) |
MySQL の SELECT では、桁があふれた DECIMAL(5,2) は最大値の 999.99 になり、20桁の数字の文字列を SIGNED に変換すると -1 になりました。SELECT の中の CAST は処理が止まらないので、警告を見ないまま集計に進むと、0 や上限値が混ざった結果になります。
長さを指定した文字列型への CAST は、PostgreSQL でもエラーにならず切り詰められます(明示的な CAST の場合。INSERT で長すぎる文字列を入れるときはエラーになります)。
MySQL でも INSERT はエラーになる(厳格モード)
MySQL 8.0 の既定の sql_mode には STRICT_TRANS_TABLES(厳格モード)が含まれています。この設定では、テーブルに書き込むときの変換はエラーになります。
-- items.qty は INT 型、created_at は DATETIME 型
INSERT INTO items VALUES (10001, 'x', 'abc', NOW());
INSERT INTO items VALUES (10002, 'x', CAST('abc' AS SIGNED), NOW());
INSERT INTO items VALUES (10003, 'x', 1, '2026-02-30 00:00:00');
ERROR 1366 (HY000) at line 29: Incorrect integer value: 'abc' for column 'qty' at row 1
ERROR 1292 (22007) at line 30: Truncated incorrect INTEGER value: 'abc'
ERROR 1292 (22007) at line 31: Incorrect datetime value: '2026-02-30 00:00:00' for column 'created_at' at row 1
| 場面 | MySQL 8.0(既定の設定) | PostgreSQL 16 |
|---|---|---|
| SELECT の中の CAST | 0 や NULL を返して警告 | エラー |
| INSERT / UPDATE での変換 | エラー(厳格モード) | エラー |
SQL Server については、公式ドキュメントに「数値でない文字データを decimal・float・int・numeric に変換するとエラーメッセージを返す」と記載されています(未実行)。
小数を整数にするときの丸め方の違い
「2.5 を整数にすると 2 か 3 か」は、データベースと元の型で変わります。
-- MySQL 8.0
SELECT CAST(2.5 AS SIGNED) a, CAST(3.5 AS SIGNED) b, CAST(-2.5 AS SIGNED) c, CAST(2.4 AS SIGNED) d,
CAST(2.5E0 AS SIGNED) e, CAST(3.5E0 AS SIGNED) f, CAST(-2.5E0 AS SIGNED) g;
-- PostgreSQL 16
SELECT CAST(2.5 AS integer) a, CAST(3.5 AS integer) b, CAST(-2.5 AS integer) c, CAST(2.4 AS integer) d,
CAST(2.5::float8 AS integer) e, CAST(3.5::float8 AS integer) f, CAST(-2.5::float8 AS integer) g;
どちらも同じ結果でした。
a | b | c | d | e | f | g
---+---+----+---+---+---+----
3 | 4 | -3 | 2 | 2 | 4 | -2
| 元の値 | MySQL 8.0 | PostgreSQL 16 | 丸め方 |
|---|---|---|---|
2.5(DECIMAL / numeric) |
3 | 3 | 四捨五入(0から遠いほうへ) |
3.5(同上) |
4 | 4 | 同上 |
-2.5(同上) |
-3 | -3 | 同上 |
2.5E0 / 2.5::float8(浮動小数点) |
2 | 2 | 最も近い偶数へ |
3.5E0 / 3.5::float8 |
4 | 4 | 同上 |
-2.5E0 / -2.5::float8 |
-2 | -2 | 同上 |
'2.5'(文字列) |
2(警告つきで切り捨て) | エラー | ― |
'2.9'(文字列) |
2(同上) | エラー | ― |
- 固定小数点(
2.5と書いた値や DECIMAL / numeric の列)は四捨五入されます。 - 浮動小数点(FLOAT / DOUBLE / float8 の列)は、ちょうど中間の値が偶数側に丸められます。 2.5 は 2、3.5 は 4 です。
- MySQL で文字列の
'2.5'をSIGNEDに CAST すると、小数点以下を読まずに 2 になります('2.9'も 2)。四捨五入したいときは、いったん DECIMAL を経由します。
SELECT CAST('2.5' AS DECIMAL(10,0)); -- MySQL: 3
SELECT CAST(CAST('2.5' AS numeric) AS integer); -- PostgreSQL: 3
SQL Server は、公式ドキュメントの表で numeric から int、float から int への変換がどちらも「切り捨て(Truncate)」とされており、CAST(10.6496 AS INT) は 10 になる例が載っています(未実行)。Oracle の丸め方は今回確認できていません。
丸め方をデータベースに任せず、意図を関数で書くと結果がそろいます。
| やりたい丸め | 書き方 | 2.9 の結果 | -2.99 の結果 |
|---|---|---|---|
| 切り捨て(0 に近いほうへ) | MySQL TRUNCATE(x, 0) / PostgreSQL trunc(x) |
2 | -2 |
| 切り下げ(小さいほうへ) | FLOOR(x) |
2 | -3 |
| 切り上げ | CEIL(x) |
3 | -2 |
| 四捨五入 | ROUND(x) |
3 | -3 |
(表の8つの値は MySQL 8.0 と PostgreSQL 16 の両方で実行して同じ結果でした。このほか MySQL で ROUND(2.5) = 3、ROUND(2.5E0) = 2 も確認しています。)
暗黙の型変換と、文字列のまま比較したときの結果
CAST を書かなくても、データベースが自動で型をそろえることがあります(暗黙の型変換)。便利な反面、意図と違う比較になることがあります。
-- MySQL 8.0
SELECT '10' < '9' a, 10 < 9 b, '10' + '9' c, '1' = 1 d, 'abc' = 0 e,
CAST('10' AS SIGNED) < CAST('9' AS SIGNED) g;
| a | b | c | d | e | g |
| 1 | 0 | 19 | 1 | 1 | 0 |
Warning (Code 1292): Truncated incorrect DOUBLE value: 'abc'
'10' < '9'は 1(真)。文字列同士の比較は先頭の文字から比べるので、'1'と'9'の比較で決まります。 数値として比べたいときは CAST します(g 列は 0)。- MySQL では
'abc' = 0が 1(真)になりました。文字列が数値 0 に変換されて比較されるためです。
PostgreSQL は暗黙の変換の範囲が狭く、型が合わない比較はエラーで知らせます。
-- PostgreSQL 16
SELECT 1 + '2' a, '10' < '9' b, '10'::int < '9'::int d; -- 3 | t | f
SELECT 'abc'::text = 0;
ERROR: operator does not exist: text = integer
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
1 + '2' が 3 になるのは、引用符で囲んだだけの値(型が決まっていないリテラル)を、相手に合わせて整数として読むためです。text 型と決まっている列や値は、自動では数値に変換されません。
型変換でインデックスが使われなくなる例を EXPLAIN で見る
WHERE 句で列の側が変換されると、その列のインデックスを使った検索ができなくなります。1万行の表で確かめました。
-- MySQL 8.0(PostgreSQL も同じ構成で作成)
CREATE TABLE items (
id INT PRIMARY KEY,
code VARCHAR(10) NOT NULL, -- 数字が入っているが文字列型
qty INT NOT NULL,
created_at DATETIME NOT NULL,
KEY idx_code (code), KEY idx_created (created_at)
);
文字列の列を数値と比べる(MySQL)
EXPLAIN SELECT * FROM items WHERE code = '1234'; -- 文字列で比べる
EXPLAIN SELECT * FROM items WHERE code = 1234; -- 数値で比べる
| type | possible_keys | key | rows | Extra |
| ref | idx_code | idx_code | 1 | NULL | ← code = '1234'
| ALL | idx_code | NULL | 10173 | Using where | ← code = 1234
Warning (Code 1739): Cannot use ref access on index 'idx_code' due to type or collation conversion on field 'code'
code = 1234 と書くと、MySQL は code 列の値を1行ずつ数値に変換して比べるため、全件走査(type: ALL)になりました。警告 1739 が、型の変換が原因でインデックスを使えないことを知らせています。引用符を付けて code = '1234' と書くだけで、インデックスを使った1行の検索になります。
逆に、整数の列に文字列を渡す id = '1234' は、定数の側が1回だけ変換されるので、主キーを使った検索(type: const)のままでした。
PostgreSQL の場合
EXPLAIN SELECT * FROM items WHERE code = 1234;
ERROR: operator does not exist: character varying = integer
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
PostgreSQL はエラーで知らせます。ここで列の側を CAST すると、MySQL と同じく全件走査になります。
| WHERE 句 | PostgreSQL 16 の実行計画 |
|---|---|
code = '1234' |
Index Scan using idx_code |
code::integer = 1234 |
Seq Scan(Filter: (code)::integer = 1234) |
id = '1234' |
Index Scan using items_pkey |
id = 1234.0 |
Seq Scan(Filter: (id)::numeric = 1234.0) |
id::text = '1234' |
Seq Scan |
CAST(created_at AS date) = '2026-03-01' |
Seq Scan |
created_at >= '2026-03-01' AND created_at < '2026-03-02' |
Index Scan using idx_created |
id = 1234.0 のように、整数の列を小数と比べたときも、列の側が numeric に変換されて全件走査になりました。
直し方
| 直す前 | 直した後 | 考え方 |
|---|---|---|
WHERE code = 1234 |
WHERE code = '1234' |
定数の型を列に合わせる |
WHERE CAST(code AS SIGNED) = 1234 |
WHERE code = CAST(1234 AS CHAR) |
変換は定数の側で行う |
WHERE CAST(created_at AS DATE) = '2026-03-01' |
WHERE created_at >= '2026-03-01' AND created_at < '2026-03-02' |
範囲の条件に書き換える |
WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2026-03-01' |
同上 | 同上 |
| どうしても列を変換して検索したい | 式インデックスを作る | 変換後の値にインデックスを張る |
MySQL でも、日付の範囲に書き換えた後は type: range、key: idx_created、rows: 24 になりました。PostgreSQL では、変換後の式にインデックスを作る方法もあります。
CREATE INDEX idx_code_int ON items ((code::integer));
EXPLAIN SELECT * FROM items WHERE code::integer = 1234;
-- Index Scan using idx_code_int on items (cost=0.29..8.30 rows=1 width=20)
「列はそのまま、変換は定数の側で」がインデックスを生かす基本です。 そのほかのパターンは、別記事「SQLのインデックスが効かない10のパターンと確認方法」で扱っています。
安全に型変換する書き方
外部から取り込んだデータなど、変換できない値が混ざる可能性があるときの書き方です。
PostgreSQL 16:pg_input_is_valid で先に確かめる
PostgreSQL 16 で追加された pg_input_is_valid(文字列, 型名) は、その型に変換できるかを真偽値で返します。
SELECT v,
CASE WHEN pg_input_is_valid(v, 'integer') THEN v::integer END AS n
FROM (VALUES ('45'), ('12abc'), (''), (' 7 '), ('99999999999')) t(v);
v | n
-------------+----
45 | 45
12abc |
|
7 | 7
99999999999 |
変換できない値は NULL になり、クエリは止まりません。桁あふれ(99999999999)も検出されています。日付や桁数つきの型にも使えます。
SELECT pg_input_is_valid('2026-02-30', 'date') AS c, -- f
pg_input_is_valid('2026-02-28', 'date') AS d, -- t
pg_input_is_valid('12345.678', 'numeric(5,2)') AS e; -- f
SELECT message, sql_error_code FROM pg_input_error_info('99999999999', 'integer');
-- value "99999999999" is out of range for type integer | 22003
15 以前では、正規表現で形を確かめてから変換します(CASE WHEN v ~ '^-?[0-9]+$' THEN v::integer END。こちらは桁あふれまでは防げません)。
MySQL:正規表現で確かめてから CAST
SELECT CASE WHEN '12abc' REGEXP '^-?[0-9]+$' THEN CAST('12abc' AS SIGNED) END AS c,
CASE WHEN '45' REGEXP '^-?[0-9]+$' THEN CAST('45' AS SIGNED) END AS d;
| c | d |
| NULL | 45 |
MySQL の CAST は '12abc' を 12 として受け入れるので、「全体が数字だけでできているか」を先に確かめることで、意図しない 0 や部分的な数値が混ざるのを防げます。日付は STR_TO_DATE が不正な値に NULL を返すので、IS NULL で検出できます。
SQL Server:TRY_CAST(公式ドキュメントで確認・未実行)
SELECT TRY_CAST('abc' AS int); -- 変換できなければ NULL
SELECT TRY_CAST('123' AS int); -- 123
公式ドキュメントには「変換に成功すれば指定した型の値を返し、そうでなければ NULL を返す」と記載されています。型の組み合わせ自体が許可されていない変換では、TRY_CAST でもエラーになります。
Oracle:DEFAULT ... ON CONVERSION ERROR(公式ドキュメントで確認・未実行)
SELECT CAST('N/A' AS NUMBER DEFAULT '0' ON CONVERSION ERROR) FROM DUAL;
SELECT TO_NUMBER('2,00' DEFAULT 0 ON CONVERSION ERROR) FROM DUAL;
変換できなかったときに返す値を、CAST や TO_NUMBER・TO_DATE の中に書けます。上の2つは公式ドキュメントに掲載されている例です。
方針のまとめ
| 状況 | おすすめ |
|---|---|
| 値の形式が保証されている | そのまま CAST |
| 変換できない値が混ざるかもしれない | 先に検査(pg_input_is_valid / 正規表現 / TRY_CAST)して NULL に |
| 取り込みデータの検証 | 変換できない行を別に抽出して件数を確認してから本処理へ |
| 検索条件 | 列を変換せず、定数の型を列に合わせる |
| 丸めが関わる | ROUND / FLOOR / TRUNC を明示する |
| 何度も CAST している列がある | 列の型そのものを見直す |
よくある質問
Q. CAST と CONVERT はどちらを使えばよいですか?
A. 型を変えるだけなら、標準SQLの CAST をおすすめします。どのデータベースでも同じ書き方で通じます。CONVERT は、SQL Server で日付の書式(スタイル番号)を指定したいとき、MySQL で文字コードを変換したいとき(CONVERT(値 USING utf8mb4))に使います。
Q. MySQL で CAST(x AS INT) がエラーになります。
A. MySQL の CAST では、整数は SIGNED または UNSIGNED、文字列は CHAR と書きます。INT・INTEGER・VARCHAR は構文エラー(ERROR 1064)になります。CAST(x AS SIGNED) に書き換えてください。
Q. 文字列の数字を数値順に並べ替えるにはどうしますか?
A. 文字列のまま ORDER BY すると '10' が '9' より前に来ます。ORDER BY CAST(code AS SIGNED)(MySQL)や ORDER BY code::integer(PostgreSQL)のように数値に変換して並べ替えます。頻繁に行うなら、列を数値型で持つことを検討します。
Q. CAST で NULL を変換するとどうなりますか?
A. NULL のままです。MySQL・PostgreSQL のどちらでも CAST(NULL AS 整数) は NULL を返しました。NULL を別の値に置き換えたいときは COALESCE(列, 0) を組み合わせます。
まとめ
CAST(値 AS 型)は標準SQL。PostgreSQL の::、MySQL と SQL Server のCONVERTは各データベース独自の書き方- MySQL の CAST は
SIGNED・CHAR・DECIMAL(p,s)と書く。INTやVARCHARは構文エラー - 書式つきの変換は、MySQL が
DATE_FORMAT/STR_TO_DATE、PostgreSQL と Oracle がTO_CHAR/TO_DATE - 変換できない値は、PostgreSQL ではエラー、MySQL の SELECT では 0 や NULL と警告。MySQL でも INSERT は厳格モードでエラーになる
- 小数→整数は、固定小数点が四捨五入、浮動小数点は中間の値が偶数側。意図は
ROUND・FLOOR・TRUNCで明示する - WHERE 句で列の側を変換するとインデックスが使われなくなる。定数の型を列に合わせる
- 安全に変換するには、
pg_input_is_valid・正規表現・TRY_CAST・ON CONVERSION ERRORで先に確かめる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SQL CASTで型変換|文字列・数値・日付の変換をDB別早見表で解説」
株式会社RJCでは、SQLの型変換とデータベースごとの違いを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。