データベースのVARCHAR列には、本来数値が入るべき項目に「abc」「N/A」「-」などの数字以外の値が混入していることがあります。CSVインポートや手入力によるデータ不備は実務で頻繁に発生し、集計や型変換でエラーの原因になります。
この記事では、SQLで指定した項目が数字以外のデータを抽出する方法を、MySQL・PostgreSQL・SQL Server・Oracleの4つのRDBMSに対応した構文で体系的に解説します。正規表現パターンの詳細、ISNUMERICの落とし穴、NULL/空文字列の扱い、データクレンジングへの活用まで、実務で必要な知識を網羅しています。
この記事で学べること
- 「数字以外のデータ」とは何か(VARCHAR列の混在データ問題)
- MySQL:REGEXP / NOT REGEXP で数字以外を抽出する方法
- PostgreSQL:~ 演算子・SIMILAR TO で非数値を判定する方法
- SQL Server:TRY_CAST / ISNUMERIC / PATINDEX の使い分け
- Oracle:REGEXP_LIKE で非数値データを検出する方法
- 整数・小数・負数・指数表記に対応した正規表現パターン
- ISNUMERICの落とし穴と安全な代替手段
- NULL・空文字列の扱い方
- 数字のみ抽出する逆パターン
- RDBMS間の構文比較テーブル
- データクレンジング・インポートデータ検証への活用
サンプルデータの準備
以下のテーブルを使って、各RDBMSでの抽出方法を解説します。price 列はVARCHAR型で、数値と非数値が混在しています。
CREATE TABLE + INSERT文(クリックで展開)
注意:price列はVARCHAR型です。数値が入るべき列をVARCHAR型で定義してしまうと、数値以外の値が格納できてしまいます。赤字の行が「数字以外のデータ」として抽出される対象です。
「数字以外のデータ」とは何か
VARCHAR(文字列型)列に格納されたデータのうち、数値として解釈できないものを「数字以外のデータ」と呼びます。具体的には以下のようなケースです。
ポイント:「数字以外」の定義はビジネス要件によって変わります。整数のみを数値とするか、小数・負数・指数表記も含めるかで、使う正規表現パターンが異なります。
MySQL:REGEXP / NOT REGEXP で数字以外を抽出する
MySQLでは REGEXP(正規表現マッチ)演算子を使って、数値パターンに一致しないデータを抽出できます。
基本:整数以外のデータを抽出する
最もシンプルなパターンです。^[0-9]+$ は「先頭から末尾まで数字のみ」にマッチするため、NOT を付けることで数字以外を抽出します。
実行結果
+----+-------+-------+----------+ | id | name | price | category | +----+-------+-------+----------+ | 2 | 商品B | abc | 飲料 | | 4 | 商品D | N/A | 日用品 | | 6 | 商品F | --- | 食品 | | 7 | 商品G | 980.5 | 飲料 | | 8 | 商品H | 未定 | 日用品 | | 9 | 商品I | -500 | 食品 | | 10 | 商品J | | 飲料 | | 12 | 商品L | 1.5e3 | 食品 | +----+-------+-------+----------+
このクエリは「整数のみ」を数値とみなすため、小数(980.5)や負数(-500)も「数字以外」として抽出されます。NULLの行(id=11)は REGEXP の結果が NULL になるため、結果に含まれない点に注意してください。
応用:小数・負数も数値として扱う
小数点や負号を含む値も数値として扱いたい場合は、正規表現パターンを拡張します。
実行結果
+----+-------+-------+----------+ | id | name | price | category | +----+-------+-------+----------+ | 2 | 商品B | abc | 飲料 | | 4 | 商品D | N/A | 日用品 | | 6 | 商品F | --- | 食品 | | 8 | 商品H | 未定 | 日用品 | | 10 | 商品J | | 飲料 | | 12 | 商品L | 1.5e3 | 食品 | +----+-------+-------+----------+
パターン ^-?[0-9]+(\.[0-9]+)?$ の各部分の意味は以下の通りです。
MySQL 8.0+:REGEXP_LIKE 関数
MySQL 8.0以降では、REGEXP_LIKE() 関数も使用できます。第3引数でフラグを指定でき、より柔軟な制御が可能です。
PostgreSQL:~ 演算子・SIMILAR TO で数字以外を抽出する
PostgreSQLでは、POSIX正規表現の ~ 演算子 または SIMILAR TO を使って非数値データを抽出できます。
~ 演算子を使う方法(推奨)
~ はPOSIX正規表現マッチ演算子で、!~ はその否定形です。
SIMILAR TO を使う方法
SIMILAR TO はSQL標準に近い正規表現マッチです。構文がやや異なります。
ポイント:SIMILAR TO では暗黙的に文字列全体がマッチ対象になるため、^ と $ は不要です。一方、~ 演算子では部分マッチなので ^ と $ で全体一致を指定する必要があります。
PostgreSQLの型キャスト方式
正規表現の代わりに、型キャストの成功/失敗で判定する方法もあります。
この方法は指数表記(1.5e3)も正しく数値と判定できる利点があります。
SQL Server:TRY_CAST / ISNUMERIC / PATINDEX で数字以外を抽出する
SQL Serverには複数の判定方法がありますが、それぞれ特性が異なります。
方法1:TRY_CAST / TRY_CONVERT(推奨)
SQL Server 2012以降で使える TRY_CAST は、変換に失敗すると NULL を返します。最も安全で推奨される方法です。
実行結果
+----+-------+-------+----------+ | id | name | price | category | +----+-------+-------+----------+ | 2 | 商品B | abc | 飲料 | | 4 | 商品D | N/A | 日用品 | | 6 | 商品F | --- | 食品 | | 8 | 商品H | 未定 | 日用品 | | 10 | 商品J | | 飲料 | +----+-------+-------+----------+
TRY_CAST は指数表記(1.5e3)も FLOAT として変換可能です。DECIMAL に変換する場合は失敗する可能性があるため、用途に応じて FLOAT または DECIMAL を使い分けてください。
方法2:ISNUMERIC 関数
ISNUMERIC() は値が数値に変換可能なら 1 を返す関数ですが、注意点が多い 関数です。
ISNUMERICの落とし穴:以下の値は ISNUMERIC = 1(数値と判定)になりますが、実際には数値として使えないことがあります。
このため、ISNUMERIC の代わりに TRY_CAST を使うことを強く推奨します。
方法3:PATINDEX(正規表現の代替)
SQL ServerにはREGEXPがありませんが、PATINDEX でパターンマッチが可能です。
PATINDEX('%[^0-9]%', price) は「数字以外の文字が含まれる位置」を返します。0 より大きければ非数値文字が存在するということです。ただし、この方法ではマイナス記号や小数点も「数字以外」と判定されるため、整数チェック向きです。
Oracle:REGEXP_LIKE で数字以外を抽出する
Oracleでは REGEXP_LIKE 関数を使って正規表現マッチングを行います。
基本:整数以外のデータを抽出する
応用:小数・負数・指数表記にも対応する
Oracleの TO_NUMBER 方式
Oracleでは DEFAULT ... ON CONVERSION ERROR(Oracle 12c R2以降)を使った安全なキャスト方法もあります。
正規表現パターン詳細:用途別まとめ
「何を数値とみなすか」によって使う正規表現パターンが異なります。以下に用途別のパターンをまとめます。
ポイント:実務では「小数・負数を含む」パターン(^-?[0-9]+(\.[0-9]+)?$)が最も汎用的です。指数表記はビジネスデータでは稀なので、通常は不要です。
NULL・空文字列の扱い
REGEXP や ISNUMERIC で判定する際、NULL と空文字列の挙動はRDBMSによって異なります。意図しない結果を避けるために、明示的に処理することが重要です。
NULLも含めて抽出する安全なクエリ
実行結果
+----+-------+-------+----------+ | id | name | price | category | +----+-------+-------+----------+ | 2 | 商品B | abc | 飲料 | | 4 | 商品D | N/A | 日用品 | | 6 | 商品F | --- | 食品 | | 8 | 商品H | 未定 | 日用品 | | 10 | 商品J | | 飲料 | | 11 | 商品K | NULL | 日用品 | | 12 | 商品L | 1.5e3 | 食品 | +----+-------+-------+----------+
注意:Oracleでは空文字列がNULLとして扱われます。他のRDBMSでは空文字列とNULLは別物です。RDBMS間でクエリを移植する際は、この違いに注意してください。
逆パターン:数字のみのデータを抽出する
「数字以外を抽出」の逆パターンとして、数字のみ(数値として有効なデータ)を抽出する方法も押さえておきましょう。
実行結果(MySQL)
+----+-------+-------+----------+ | id | name | price | category | +----+-------+-------+----------+ | 1 | 商品A | 1500 | 食品 | | 3 | 商品C | 2400 | 食品 | | 5 | 商品E | 3200 | 飲料 | | 7 | 商品G | 980.5 | 飲料 | | 9 | 商品I | -500 | 食品 | +----+-------+-------+----------+
RDBMS間比較テーブル
各RDBMSで「数字以外のデータを抽出する」ための構文を比較します。
実務活用:データクレンジングへの応用
数字以外のデータを抽出するテクニックは、データクレンジング(データ品質の改善)に直結します。ここでは実務でよくあるパターンを紹介します。
パターン1:不正データの件数を把握する
実行結果
+------------+---------------+---------------+ | total_rows | numeric_count | invalid_count | +------------+---------------+---------------+ | 12 | 5 | 7 | +------------+---------------+---------------+
パターン2:インポートデータの検証
CSVファイルを一時テーブルにインポートした後、本テーブルに格納する前に検証するパターンです。
パターン3:カテゴリ別の不正データ率を算出
実行結果
+----------+-------+---------+-------------+ | category | total | invalid | invalid_pct | +----------+-------+---------+-------------+ | 食品 | 5 | 2 | 40.0 | | 飲料 | 4 | 1 | 25.0 | | 日用品 | 3 | 2 | 66.7 | +----------+-------+---------+-------------+
応用:英数字以外のデータを抽出する
数字だけでなく、英数字(アルファベット + 数字)以外のデータを抽出したい場合もあります。文字コード体系のバリデーションに有用です。
よくあるエラーと対処法
前後スペースへの対処
まとめ
SQLで指定した項目が数字以外のデータを抽出する方法を、4つのRDBMSに対応した形で解説しました。
数字以外のデータの抽出は、データクレンジングやインポートデータの品質チェックで欠かせないテクニックです。正規表現パターンはビジネス要件に応じて「整数のみ」「小数含む」「負数含む」と段階的に拡張できます。ISNUMERICの落とし穴に注意し、SQL Serverでは TRY_CAST を使うことを推奨します。
関連記事:WHERE句の使い方と複数条件の書き方 / LIKE演算子でのパターンマッチ / DISTINCTで重複データを削除する方法
