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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?