IsNumericで数値チェックしたのに、エラーになる。
Accessで作った入力画面を使っていただいていたところ、面積を入力して次の項目へ移動したときにエラーが出る、という連絡をいただきました。
実行時エラー ‘3075’: クエリ式 ’09-‘ の 構文エラー : 演算子がありません。
保存処理では、入力値が数値かどうかを IsNumeric で判定してからSQLに埋め込んでいました。ところが「09-」という入力が数値と判定されて通り抜け、そのままSQLに埋め込まれてエラーになっていました。
数量・面積・金額などの入力欄
非連結のテキストボックスに入力させ、VBAで保存している
入力チェックにIsNumericを使っている
数値でなければメッセージを出して止めている
入力値をSQLに組み立てて実行している"UPDATE ... SET 数量=" & Me.Txt_Suryo のように連結している
IsNumericが「数値」と判定する文字列
IsNumericは「数値に変換できるかどうか」を判定する関数です。「数字だけでできているか」を判定するものではありません。実際にいろいろな文字列で試してみました。あわせて、Val と CDbl で変換した結果も載せています。
| 入力 | IsNumeric | Val | CDbl |
|---|---|---|---|
123 | True | 123 | 123 |
-123 | True | -123 | -123 |
1.5 | True | 1.5 | 1.5 |
09- | True | 9 | -9 |
1- | True | 1 | -1 |
+5 | True | 5 | 5 |
5+ | True | 5 | 5 |
1d2 | True | 100 | 100 |
1e3 | True | 1000 | 1000 |
&H10 | True | 16 | 16 |
&O17 | True | 15 | 15 |
123(全角) | True | 0 | 123 |
12 (前後に空白) | True | 12 | 12 |
12,345 | True | 12 | 12345 |
1,2,3 | True | 1 | 123 |
\100 | True | 0 | 100 |
(5) | True | 0 | -5 |
1.2.3 | False | 1.2 | エラー |
1-2 | False | 1 | エラー |
- | False | 0 | エラー |
. | False | 0 | エラー |
| (空文字) | False | 0 | エラー |
※ Windows 11・Access 2021(日本語)で確認しました。カンマや「\」の扱いは、Windowsの地域の設定によって変わる可能性があります。
気をつけたいのは次の点です。
– 末尾のマイナス・プラス(09-、5+)も数値と判定されます。CDbl("09-") は -9 になります
– d や e を使った指数表記(1d2 = 1×10²)、&H(16進数)・&O(8進数)も数値です
– カンマ・円記号・かっこも通ります。CDbl("(5)") は会計表記の負の数として -5 になります
– Val は数字以外の文字が出たところで読むのをやめるので、12,345 は 12、全角の 123 は 0 になります。エラーにならない代わりに、黙って違う値になります
そのままSQLに埋め込むとエラー3075
IsNumericを通った値を、そのままSQLに連結すると次のようになります。
CurrentDb.Execute "UPDATE T_受注明細 SET 数量=" & "09-" & " WHERE ID=1", dbFailOnError| 埋め込んだ値 | 結果 |
|---|---|
09- | エラー3075「クエリ式 ’09-‘ の 構文エラー : 演算子がありません。」 |
1d2 | エラー3075(同上) |
&H10 | エラー3075(同上) |
VBAでは数値として読めても、SQLでは読めない書き方があるためです。
数字・マイナス・小数点だけを許可するチェック関数
標準モジュールに以下のコードを貼り付けます。
'=========================================================
' プロシージャ名:gf_NUM_CHECK
' 機能 :入力値が数値かどうかを判定します。
' 許可するのは「先頭のマイナス」「数字」「小数点1つ」だけです。
' 全角の数字は半角に直してから判定します。
'
' 【引数】
' strValue :判定する文字列
'
' 【戻り値】
' True :数値 / False :数値ではない
'
' 【使用例】
' gf_NUM_CHECK("12.5") → True
' gf_NUM_CHECK("09-") → False
'=========================================================
Public Function gf_NUM_CHECK(ByVal strValue As String) As Boolean
Dim l_str As String ' 判定する文字列
Dim l_pos As Long ' 文字の位置
Dim l_ch As String ' 1文字
Dim l_dot As Boolean ' 小数点が出てきたか
gf_NUM_CHECK = False
'=== 全角→半角、前後の空白を除去 ===
l_str = Trim(StrConv(strValue, vbNarrow))
If l_str = "" Then Exit Function
'=== 先頭のマイナスだけ許可 ===
If Left(l_str, 1) = "-" Then l_str = Mid(l_str, 2)
If l_str = "" Then Exit Function
'=== 1文字ずつ確認 ===
For l_pos = 1 To Len(l_str)
l_ch = Mid(l_str, l_pos, 1)
If l_ch = "." Then
If l_dot Then Exit Function ' 小数点が2つ目
l_dot = True
ElseIf l_ch < "0" Or l_ch > "9" Then
Exit Function ' 数字以外
End If
Next l_pos
'=== 「.5」「5.」のような形は不可 ===
If Left(l_str, 1) = "." Or Right(l_str, 1) = "." Then Exit Function
gf_NUM_CHECK = True
End Function上の表の文字列でこの関数を試すと、Trueになるのは 123、-123、1.5、123(全角)、 12 (前後に空白)の5つだけでした。09- や 1d2、12,345 などはすべてFalseになります。
カンマ区切りを許可したい場合
金額欄などで「12,345」を受け付けたい場合は、判定の前に l_str = Replace(l_str, ",", "") を入れます。ただしその場合は、保存するときにもカンマを取り除いてからSQLに渡してください。
実行方法
入力欄の「更新前処理」で呼び出し、数値でなければ Cancel = True で更新を取り消します。
Private Sub Txt_Menseki_BeforeUpdate(Cancel As Integer)
'=== 空欄は許可(必須にしたい場合はここで止める)===
If Len(Me.Txt_Menseki.Text) = 0 Then Exit Sub
If Not gf_NUM_CHECK(Me.Txt_Menseki.Text) Then
MsgBox "数値で入力してください。", vbExclamation
Cancel = True
End If
End SubCancel = True にすると、カーソルはその入力欄に残ったままになります。Escキーを押すと、入力前の値に戻ります。
入力中の文字列は .Text で取得しています。フォーカスがある入力欄の、画面に表示されている文字列そのものを判定するためです。
保存するときは、全角で入力された場合に備えて StrConv(Me.Txt_Menseki.Text, vbNarrow) で半角に直してからSQLに渡します。
最後に
以上がAccessVBAのIsNumericの落とし穴と、入力チェック関数の作り方です。
IsNumericは「VBAで数値に変換できるか」を見る関数なので、09- や 1d2 のような、人が見ると数値に見えない文字列も通してしまいます。入力値をSQLに埋め込んだり、計算に使ったりする場合は、許可する文字を決めて1文字ずつ確認するチェックにしておくと安全です。

コメント