テーブル更新でエラー発生

解決


green_color  2008-08-02 02:01:12  No: 140169

[VB6]
[WindowsXP]

初めて質問します。
VB6にてデータベース操作をするプログラムを組んでいるのですが、
フォーム上にテーブルの各フィールドに対応したテキストボックスが
配置してあり、そこへ手入力でデータを書き込み、同じくフォーム上
にある「更新」ボタンを押すと

「型が一致しません。」

というエラーになってしまい、テーブルの更新ができません。
http://homepage1.nifty.com/rucio/main/database/db4.htm
ここの「2.レコードの修正」の方法を利用し、

  rs("番号").Value = TextNo.Text
  rs("名称").Value = TextName.Text
  rs("ランク").Value = TextRank.Text
  rs("説明").Value = TextExp.Text
  rs("詳細").Value = TextDetail.Text
  rs("特典").Value = TextReward.Text
  rs.Update

として実行したのですが上記エラーが発生して更新失敗。
それならばと

  cn.Execute "UPDATE 称号一覧 SET 番号 = " & TextNo.Text & _
          ", 名称 = '" & TextName.Text & "', ランク = '" _
          & TextRank.Text & "', 説明 = '" & TextExp.Text & _
          "', 詳細 = '" & TextDetail.Text & "', 特典 = '" _
          & TextReward.Text & "'"

と直接SQLを実行させてみようとしましたが、
こちらでも型が一致しないエラーが発生してしまいます。
(ただ、何故かこちらだとテーブルの更新は出来ているようですが…?)
ちなみにValやCInt等を使用しても結果は同じでした。

確かに、ウォッチ式で確認してみるとテーブルの各フィールドの型は
"Valiant/Integer"や"Valiant/String"など、よく分からない表記に
なっており、単なる数値や文字列とは違うように見受けられますが…。
一体、どのようにすればテーブルとテキストボックスの型を一致
させられるのでしょうか?


通りすがり  2008-08-02 17:12:47  No: 140170

まず変数使わず更新できるように確認しましたか?


  2008-08-02 22:38:59  No: 140171

>"Valiant/Integer"や"Valiant/String"など
つー事は、少なくともDBには数値として設定しているフィールドが
あるって事だよね
Textプロパティをそのまま渡しても数値にはならないよ
CInt、CLng等で変換しないと
(どの変換を使うかは、DBのフィールド型による。
  データ長を、VB6側(Integerだったら16bit、とか)とDB側で
  合わせることを念頭に置いて処理するのがベターだと思う)

また、UPDATE文を直書きする場合、数値のフィールドに値を設定する場合は
シングルクォートは付けてはいけない


green_color  2008-08-05 01:20:18  No: 140172

通りすがりさん、あさん、ご回答ありがとうございます。

あれからまた色々と調べてみて分かったのですが、結論から言うと
半分は自己解決、と言ったところでしょうか。
というのは、先に挙げた2つの方法のうち2番目のUPDATE文を直接
書いて実行するやつで発生した「型が違います」エラーなんですが、
実はあれはUPDATE文自体ではなく、その後のエラー処理に問題が
あったのが原因だったのです。
つまり、この更新ボタン処理の最初で"On Error Goto ErrUPD_Sub"
として何かエラーが発生した場合にアプリが落ちるのを回避する
ような形にしているのですが、その回避処理というのがエラー内容を
メッセージボックスに表示するというもので、そのMsgBoxの構文に
間違いがあってそこでエラーになって落ちていたのです。
これに関してはMsgBoxの構文エラーを修正して事無きを得たのですが、
しかしUPDATE文実行後にエラー処理に飛ぶということはやはりUPDATE
の実行時にエラーが発生しているわけで、その内容というのが
  -2147467259
  インデックス、主キー、またはリレーションシップで値が重複しているので、テーブルを
  変更できませんでした。重複する値のあるフィールドの値を変更するか、インデックスを
  削除してください。または重複する値を使用できるように再定義してください。
  Microsoft JET Database Engine
ということなんですが、今回扱っている「称号一覧」というテーブルには
「番号」という数値属性の項目があって、これがプライマリキー(ユニーク)
に設定されております。
#ちなみにその他の項目は全て文字列属性です。
で、このフォーム上には「名称」を指定して検索ボタンを押すことでその
名称に該当するレコードの情報をフォーム上の各テキストボックスに表示
するという機能もあり、こちらに関しては全て問題なく動作することが
確認されており、このとき検索したレコードにカーソルが位置付いている
こともしっかり確認しました。
その更新対象のレコードを検索後に先ほどのUPDATE文による更新処理を
実行したところ上記のようなエラーが発生し、その原因を探っていたところ、
どうもUPDATE文には更新するレコードをWHERE句によって指定することが
可能なようで、それを指定していなかったがためにエラーになっていた
という結論に達し、実際に先のUPDATE文の後に
  "WHERE 番号 = " & TextNo.Text
と付け加えてみたところ、見事に更新処理が成功しました。
とまぁこれはこれで一応解決したのですが、もうひとつの方法である
RecordSetを使用して更新するやつですが、こちらは依然として最初の
  rs("番号").Value = TextNo.Text
の実行時にエラーになり、そのときに表示されるメッセージボックスの
内容は「型が違います」となっています。
そこでUPDNoというInteger属性の変数を用意して
  UPDNo = TextNo.Text
  rs("番号").Value = UPDNo
としてみたのですが、やはりこれでも同じエラーになってしまいました。
それぞれの型を確認すると
  rs("番号").Value  →  Variant/Integer
  UPDNo             →  Integer
それ以外の項目(名称〜特典)に関しては左辺がVariant/String、右辺が
Stringとなっています。
なぜ、このようなエラーになってしまうのでしょうか?
Variant/IntegerとIntegerはその見た目通り違う型なのでしょうか?
だとすればこれはどのように回避すればよいのでしょうか?
というか、回避策が存在するのでしょうか?
もし無いのであればもうUPDATE文直書き方式で行くしかないのでしょうか?

大変長くなってしまいましたが、何卒参考意見を頂けるよう、
宜しくお願い致します。


特攻隊長まるるう  2008-08-05 02:41:30  No: 140173

>どうもUPDATE文には更新するレコードをWHERE句によって指定することが
>可能なようで、それを指定していなかったがためにエラーになっていた
>という結論に達し、実際に先のUPDATE文の後に
>  "WHERE 番号 = " & TextNo.Text
ここが半分しか理解できていないです。
。。。説明できていないだけかもしれませんが。
データベース更新でWHERE句によって更新レコードを指定しなかった場合、
全てのレコードが更新対象となります。

もっと言えば、WHERE句の条件に合うレコードが複数あれば、その複数の
レコード全てが更新されます。今回の場合、主キー重複違反のメッセージ
ですので、全てのレコードを同じ「番号」にしようとしてエラーになった
のです。ですから「番号」の主キーを解除しても更新は成功します。ただし、
全てのレコードが同じデータで更新されてしまいますが。
このことを理解し、どのレコードを更新するか理解した上で更新処理を
作る必要があります。

また、一般に主キーはアプリケーションから更新しません。サンプルでも
    '■I  更新処理
の部分に主キーは無いでしょう?これは意味があってそうなっているのです。
主キーは他テーブルとの結び付けに重要な場合が多く、勝手に変えると
システムが動かなくなる可能性が高いのです。データベース管理者レベルの
知識を持った人のみが更新すべきです。
そして、アプリケーションでその機能を実装する場合は、必ず事前に
検索処理で主キー重複チェックをしてください。

>なぜ、このようなエラーになってしまうのでしょうか?
>Variant/IntegerとIntegerはその見た目通り違う型なのでしょうか?
そこが原因で無いような気がします。
>つまり、この更新ボタン処理の最初で"On Error Goto ErrUPD_Sub"
>として何かエラーが発生した場合にアプリが落ちるのを回避する
>ような形にしているのですが、その回避処理というのがエラー内容を
>メッセージボックスに表示するというもので、そのMsgBoxの構文に
>間違いがあってそこでエラーになって落ちていたのです。
これと同じバグでは?

とりあえず、データベースに登録されていない「番号」で更新して
みては?それで通るようなら、本当は主キー違反のメッセージが出て
いるのに、どこかで握りつぶして違うメッセージを出しているのでしょう。

> rs("番号").Value = TextNo.Text
レコードの追加ではなく、修正でこのコードを実行しているのですよね?
rs のカレントレコードを変更しようとしていることを理解してますか?


green_color  2008-08-06 01:05:52  No: 140174

特攻隊長まるるうさん、ありがとうございます。

>データベース更新でWHERE句によって更新レコードを指定しなかった場合、
>全てのレコードが更新対象となります。

>もっと言えば、WHERE句の条件に合うレコードが複数あれば、その複数の
>レコード全てが更新されます。今回の場合、主キー重複違反のメッセージ
>ですので、全てのレコードを同じ「番号」にしようとしてエラーになった
>のです。ですから「番号」の主キーを解除しても更新は成功します。ただし、
>全てのレコードが同じデータで更新されてしまいますが。

なるほど、そういうことでしたか。
全レコードが更新の対象に…それは大変危ないですね。

>とりあえず、データベースに登録されていない「番号」で更新して
>みては?それで通るようなら、本当は主キー違反のメッセージが出て
>いるのに、どこかで握りつぶして違うメッセージを出しているのでしょう。

>> rs("番号").Value = TextNo.Text
>レコードの追加ではなく、修正でこのコードを実行しているのですよね?
>rs のカレントレコードを変更しようとしていることを理解してますか?

おっしゃるとおりでした。いや誠にお恥ずかしい…。
で、よくよく調べてみたらエラーNo.3251が出ていることが判明しました。
要するにこの環境ではRecordSetオブジェクトを使ったテーブル更新は
出来ないということみたいですね。
今回の更新処理は飽くまでカレントレコードを更新することが目的なので
更新処理時は「番号」テキストボックスを変更不可の状態にして、
内部処理はUPDATE直書き方式(WHERE句でレコード指定)にて行うという
方法を取ることにしました。

大変お騒がせしました。
ありがとうございました。


特攻隊長まるるう  2008-08-06 01:34:02  No: 140175

>全レコードが更新の対象に…それは大変危ないですね。
Delete 文でも同じですから、テーブル1つのデータを丸ごと消してしまう大ポカ
やった経験のある新人が、どの会社にも1人は居るものです。

>で、よくよく調べてみたらエラーNo.3251が出ていることが判明しました。
>要するにこの環境ではRecordSetオブジェクトを使ったテーブル更新は
>出来ないということみたいですね。
ん?『実行時エラー'3251'  オブジェクトまたはプロバイダは要求された操作を実行できません。』
ですか?で、その原因は?データベースの設定の違いですか?

カーソルはちゃんとサンプルコード通り adOpenDynamic, adLockOptimistic
の組合せで開いてますか?サンプルコードで出来て、自分のプログラムで出来ない
部分は、しっかり原因追求しておかないと、また誤解してる可能性が高いですよ。

正しいエラーメッセージでも原因を直接説明していない場合があります。
正確なエラーメッセージで WEB 検索すると、過去の事例などがたくさん
引っ掛かります。本当に回避不能な状況なのか?ざっとでいいので過去の
事例に目を通しておくことをお勧めします。

UPDATE 直書き方式で出来るなら RecordSet でも出来ると思うんですが。。。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加