動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

SQL Server の TRY_CONVERT / TRY_CAST で変換失敗を NULL にしてバッチを止めない

バイブス父さん
現役の業務SE
2026年7月14日13 min read広告 (PR) を含む場合があります
SQL Server の TRY_CONVERT / TRY_CAST で変換失敗を NULL にしてバッチを止めない

みなさんこんにちは!ヒロポンです!

画面から入力された文字列を数値や日付に変換するバッチ。書いてて、変な値が1個混ざってた瞬間にジョブが丸ごと落ちた、って経験ないですか??

ありますよね。多分あるはず。

「金額」のはずのカラムに N/A とか全角スペースとか、想定してない文字が入ってる。それを CAST で数値に変換した瞬間、その1行だけじゃなくバッチ全体がエラーで止まる

夜間バッチだと、翌朝それに気づく。しんどいやつです。

ここで効くのが SQL Server の TRY_CONVERT / TRY_CAST。変換できない値を例外じゃなく NULL で返してくれるので、汚いデータが混ざってもバッチが止まらない。

この記事では、業務でこの手の型変換をやる時に踏みがちな落とし穴を5つ、実テーブルで動かしながらまとめました。

忙しい人向けに最初にまとめ

  • CAST / CONVERT は変換できない値が1行でもあるとステートメント全体がエラーで止まる
  • TRY_CONVERT / TRY_CAST は変換できない値を NULL にして、他の行はそのまま流す
  • ただし NULL が返るので、後続の WHERE / 集計 / JOINNULL を考慮しないと結果がズレる
  • TRY_CONVERTSQL Server 2012 以降。書式(style)を指定したいなら TRY_CONVERT、不要なら TRY_CAST
  • 変換不能な値だけでなく、範囲オーバーtinyint に 300 等)も NULL になる

以下、汚いデータの入ったテーブルで実際に動かしていきます。

そもそも何が起きるのか — CAST は1行の失敗で全部落ちる

まず問題を再現します。画面入力を溜めた、ちょっと汚いテーブルを用意する。

CREATE TABLE #Input (Id INT, RawValue VARCHAR(20));
INSERT INTO #Input VALUES
    (1, '100'),
    (2, '200'),
    (3, 'N/A'),   -- 変換不能な値
    (4, ''),      -- 空文字
    (5, '300');

これを CASTINT に変換してみる。

SELECT Id, CAST(RawValue AS INT) AS Value
FROM #Input;

実行結果:

CAST で汚いデータを INT 変換した実行結果 — 100 と 200 は返るが N/A の行で Msg 245 の変換エラーになりステートメントが異常終了する

N/A の行に当たった瞬間、そのステートメントは Msg 245(変換エラー)で異常終了します。100200 は先に返っていても、ステートメントとしては失敗

INSERT ... SELECT みたいなバッチなら、その1件のせいで1行もコミットされません。ん?1行のために全部巻き添え?ってなりますよね。

でもこれが夜間バッチの中で起きると、そのジョブが丸ごと止まる。

同じことを TRY_CONVERT でやると、こんな感じで変わる。

SELECT Id, TRY_CONVERT(INT, RawValue) AS Value
FROM #Input;

実行結果:

TRY_CONVERT で同じデータを変換した実行結果 — N/A は NULL、空文字は 0、変換できる行はそのまま、全5行が返る

N/A の行は NULL、変換できる行はそのまま。全行返ってきて、バッチは止まらない。これがやりたかったこと!! いい感じです。

ここで実行結果をよく見てください。空文字('')の行は NULL じゃなく 0 になってます。

空文字は「変換できない値」ではなく「0 に変換できる値」扱いなんですね。NULL を期待してると足をすくわれるやつ。頭の隅に置いておいてください(落とし穴3でもう一度出てきます)。

では、この置き換えで実務がハマる点を順番に見ていきます。

⏱ 対処の早見表

先に、各落とし穴の「気づいてから直すまでの目安」を置いておきます。

落とし穴 症状 対処の目安
1. CAST が全部落ちる バッチが1件も処理せず停止 TRY_ 化で即・5分ほど
2. バージョン非対応 'TRY_CONVERT' is not a recognized... CASE や ISNUMERIC で代替・30分ほど
3. NULL の考慮漏れ 集計や件数が静かにズレる WHERE や ISNULL を追加・15分ほど
4. style 指定ミス 日付が意図と違う / NULL 多発 style 番号を確認・10分ほど
5. 範囲オーバー 想定より NULL が多い 変換先の型を見直す・15分ほど

1. CAST/CONVERT は変換失敗でステートメントごと落ちる

これが今回の主役の落とし穴です。上で見たとおり、CASTCONVERT は変換できない値が1行でもあると、その行だけでなくステートメント全体をエラーで止めます。

業務のバッチは何万行も一括で処理する。たった1件のゴミデータでジョブ全体が死ぬわけです。

俺も昔、月次の取込バッチが1レコードの N/A で止まって、翌朝までデータが入ってなかったことがある。原因のレコードを特定するのに、ログを頭から追う羽目になりました。地味に効くやつ。

対処は、変換を TRY_CONVERT(または TRY_CAST)に置き換えるだけ。

CAST を使ったバッチ INSERT を TRY_CONVERT に置き換えて変換失敗行を NULL で流す差分

教訓: 外部由来の汚いデータを変換するなら、既定を TRY_ にする。落ちてほしいのは例外的な場面だけです。

CAST / CONVERT そのものの基本的な落とし穴(暗黙変換や桁あふれ)は「SQL Server の CAST/CONVERT で踏む3つの罠」にまとめてあります。

これは前段の話なので、興味がある人は別タブ開いて後で読んでくださいな。

2. TRY_CONVERT / TRY_CAST は SQL Server 2012 以降

便利なんですが、TRY_ 系は SQL Server 2012 で追加された関数です。2008 R2 以前のインスタンスでは、そもそも関数が認識されずにエラーになる。

レガシーな現場だと SQL Server 2008 のまま、というのは普通にある。ここは前提を確認してから使ってください。

バージョンはこれで分かります。

SELECT SERVERPROPERTY('ProductVersion') AS Version,
       SERVERPROPERTY('ProductLevel')   AS Level;

2008 R2 以前で同じことをやりたいなら、CASE 式と ISNUMERIC で自前ガードする手があります。

SELECT Id,
       CASE WHEN ISNUMERIC(RawValue) = 1
            THEN CAST(RawValue AS INT)
            ELSE NULL END AS Value
FROM #Input;

ただし ISNUMERIC'1e5''$100''.' みたいな「数値っぽいけど INT にできない値」も 1 を返す曲者。これはこれで別のハマりを生みます。

2012 以降なら素直に TRY_CONVERT の方が安全です。

教訓: TRY_ 系が使えるかはバージョン次第。使う前に ProductVersion を1回確認する。

3. NULL が返るので後続の WHERE / 集計 / JOIN でズレる

TRY_CONVERT は「エラーを消す」んじゃなくて「エラーを NULL に変える」関数です。ここを勘違いすると、静かに結果がズレる。

これが2つ目の大きなハマりどころ。

たとえば「変換した金額の合計」を出すとき。

-- NULL は SUM で無視される → 変換できなかった行は集計から静かに漏れる
SELECT SUM(TRY_CONVERT(INT, RawValue)) AS Total
FROM #Input;

SUMNULL を飛ばして計算します(厳密には Null value is eliminated by an aggregate という警告が出ますが、エラーにはならない)。

なので変換できなかった N/A の行は集計からこっそり抜ける。おまけに、さっきの空文字は 0 として足し込まれる。

件数が合わない、金額が微妙に足りない。その原因がこのあたりだったりします。これが罠の正体。

「変換できなかった行」を弾きたい、あるいは拾いたいなら、NULL を明示的に扱う。

-- 変換できた行だけを対象にする
SELECT Id, TRY_CONVERT(INT, RawValue) AS Value
FROM #Input
WHERE TRY_CONVERT(INT, RawValue) IS NOT NULL;

-- 変換できなかった行を 0 とみなす
SELECT Id, ISNULL(TRY_CONVERT(INT, RawValue), 0) AS Value
FROM #Input;

JOIN のキーを TRY_CONVERT で作る場合も同じ。NULL 同士は結合しないので、変換に失敗したキーの行はごっそり結合対象から外れます。

教訓: TRY_ は例外を NULL に化かすだけ。NULL をどう扱うかは自分で決める。

4. TRY_CAST と TRY_CONVERT の違い(style 指定の有無)

TRY_CASTTRY_CONVERT、どっちを使えばいいのか?? 迷ったらこれだけ覚えてください。書式(style)を指定したいかどうかです。

  • TRY_CAST(値 AS 型)CAST と同じ。style は渡せない
  • TRY_CONVERT(型, 値, style)CONVERT と同じ。第3引数に style を渡せる

日付変換で効いてきます。'2026/07/14' みたいなスラッシュ区切りの書式を明示したい時、TRY_CONVERT なら style 番号で「この書式で読んで」と指定できる。

-- style 111 = yyyy/mm/dd (日本でよく使う書式)
SELECT TRY_CONVERT(DATE, '2026/07/14', 111) AS Parsed;

こんな感じで、書式を明示したうえで安全に日付へ変換できます。

ただし style を間違えると、変換自体は通っても意図と違う日付になったり、NULL が多発したりします。日付まわりの style は思い込みで書かず、Microsoft Learn の style 一覧を1回見て確認するのが安全です。

教訓: style を渡したいなら TRY_CONVERT、いらないなら TRY_CAST。日付の style は必ず裏を取る。

5. 変換不能だけでなく範囲オーバーも NULL になる

最後。TRY_CONVERTNULL を返すのは N/A みたいに変換できない値だけ、と思ってると足をすくわれます。変換はできるけど型の範囲を超える値NULL になります。

分かりやすいのが tinyinttinyint は 0〜255 しか入らないので、'300' は「数字だけど範囲外」です。

SELECT TRY_CONVERT(TINYINT, '300') AS Result;  -- 範囲外 → NULL
SELECT TRY_CONVERT(TINYINT, '200') AS Result;  -- 範囲内 → 200

CAST('300' AS TINYINT) ならオーバーフローのエラーになるところを、TRY_CONVERT は静かに NULL にします。

エラーで気づけない分、「なんか NULL が多いな」の原因が範囲オーバーだった、というのは起きがち。これもタチが悪い。

教訓: NULL が想定より多い時は、「変換不能」だけでなく「変換先の型が狭すぎないか」も疑う。

まとめ・チートシート

汚いデータの型変換で踏む5つを見てきました。最後に3つの関数を一枚で。

CAST / TRY_CAST / TRY_CONVERT の変換失敗時の挙動・style指定可否・対応バージョンの比較表

要は、外部由来の汚いデータを流し込むバッチでは、既定を TRY_CONVERT / TRY_CAST にしておく。

そのうえで、返ってくる NULLWHEREISNULL でどう扱うかまで決めておく。ここまでやれば、N/A 1個でジョブが止まる事故はなくなります。

TRY_ 系を押さえておくと、汚いデータが来る前提の現場でも夜中に叩き起こされる回数が確実に減る。そこが一番の効きどころです。

よくある質問

TRY_CONVERT はパフォーマンスが遅くなりませんか

変換1回あたりのコストは CONVERT とほぼ変わりません。ただし WHERE TRY_CONVERT(...) IS NOT NULL のように条件で使うと、その列のインデックスが効きにくくなる(式が絡むと SARGable でなくなる)ことはあります。大量データで条件に使うなら、変換済みの列を用意する等の設計を検討してください。

TRY_CONVERT で NULL になった行だけ後で調べたいです

元の文字列を残しておいて、WHERE TRY_CONVERT(INT, RawValue) IS NULL AND RawValue IS NOT NULL で「値はあるのに変換できなかった行」を抽出できます。取込バッチではこの結果をエラーテーブルに退避しておくと、後追いが楽になります。

空文字や NULL を変換するとどうなりますか

TRY_CONVERTNULL を渡すと結果も NULL です。一方で空文字('')は、INTDECIMAL などの数値型では NULL ではなく 0 に変換されます(実測)。「変換できない=NULL」のつもりで空文字を見落とすと、集計に 0 が紛れ込んで件数や合計がズレます。ちなみに ISNUMERIC('')0(数値でない扱い)なので、ISNUMERIC ガードと TRY_CONVERT では空文字の結果が食い違う点にも注意です。

ISNUMERIC ではダメなのですか

ISNUMERIC は「数値として解釈できるか」を返しますが、'1e5''$100''.' even も 1(数値扱い)を返すので、INT に変換できることの保証にはなりません。2012 以降なら TRY_CONVERT(INT, ...) IS NOT NULL で「INT に変換できるか」を直接判定する方が正確です。

次に読むべき記事

以上!

同じ「N/A 1個でバッチ全滅」を踏んだ人がいたら、どんどんシェア待ってるぜ!!


執筆者

バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について