こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

[VBA] ADOの Clone と AddNew

Access VBA 学習中の初心者です。ADOについて教えて下さい。

RecordsetオブジェクトのCloneメソッドを使用してレコードのコピーを行う以下のプロシージャで、
If Not rs.EOF Then
の存在意義がわかりません。

1.
rsClone に対象のレコードがあった場合は次行からの処理をする、ということであれば
If Not rsClone.EOF Then
にすれば良いかと思うのですが、それでは違いますでしょうか?

2.
とある人に聞いたところ、
「rsのカレントがEOFだとAddNewでエラーになるから、そのエラー回避のためそうなっている」
と言われたのですが、
テキストやヘルプで AddNewメソッド についてそのような注意事項を見つけられませんでした。
自分なりに試してみてもその条件下でエラーになりませんでした。(試し方に自信ないですが。)
AddNewメソッドを使う際にEOFであるかどうか気にした方が良いものなのでしょうか?

質問が分かりづらく、ご回答頂くにあたり追加の情報が必要でしたらご指摘下さい。
「プロシージャをこうした方がわかりやすい、適切」などのご意見も歓迎です。
どうぞよろしくお願いいたします。
--------------------------
Dim cn As ADODB.Connection
Dim rs As New ADODB.Recordset
Dim rsClone As ADODB.Recordset
Dim myField As Variant, cnt As Integer

Set cn = CurrentProject.Connection
rs.Open "名簿", cn, adOpenStatic, adLockOptimistic

Set rsClone = rs.Clone(adLockReadOnly)
rs.Clone.Bookmark = rs.Bookmark
rsClone.Find "名簿 like '山田 太郎'"

If Not rs.EOF Then                 ←★★★質問★★★

rs.AddNew

cnt = 0

For Each myField In rsClone.Fields
rs.Fields(cnt) = myField
cnt = cnt + 1
Next

End If

rs.Update

rs.Clone.Close
Set rsClone = Nothing
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing

End Sub

投稿日時 - 2013-04-23 23:52:35

QNo.8056811

困ってます

質問者が選んだベストアンサー

我流の私で良ければ・・・(正しいのかは、専門家に聞いていただくとして)

1)rsClone での判別が正しいと思います。
  ただし、> 次行からの処理 ではないと思います。
2)そのような話は聞いた事がありません。

VBA記述で気になった点

a)私なら、こう記述します

> cnt = 0
> For Each myField In rsClone.Fields
>   rs.Fields(cnt) = myField
>   cnt = cnt + 1
> Next



For cnt = 0 To rs.Fields.Count - 1
  rs.Fields(cnt) = rsClone.Fields(cnt)
Next


b)変数名に気をつけます

rsClone → rsC

つまり、ドット?ピリオド?( . )の、あり/なしで同じ綴りになるものは使いません。
以下の ★ 部分の記述は間違いですよね

> rs.Clone.Close ' ★
> Set rsClone = Nothing


参考になるかどうかわかりませんが、私が記述したとしたら以下の様な感じに
(未検証ですので、雰囲気で・・・ということに)

Dim rs As New ADODB.Recordset
Dim rsC As ADODB.Recordset
Dim i As Long

rs.Source = "SELECT * FROM 名簿 WHERE 名簿 = '山田 太郎';"
rs.Open , CurrentProject.Connection, adOpenStatic, adLockOptimistic
If (Not rs.EOF) Then
  Set rsC = rs.Clone
  rsC.AddNew
  For i = 0 To rs.Fields.Count - 1
    rsC(i) = rs(i)
  Next
  rsC.Update
  rsC.Close
  Set rsC = Nothing
End If
rs.Close

rs を得る時にコピー元を抽出しておきます。
なかったら処理しない様に・・・・
なお、コピーする中で、オートナンバ「an」を除外するとしたら

  For i = 0 To rs.Fields.Count - 1
    Select Case rs.Fields(i).Name
      Case "an"
      Case Else
        rsC(i) = rs(i)
    End Select
  Next

の様にすると思います。

※ rsC(i) = rs(i) は rsC.Fields(i) = rs.Fields(i) でも


上記は単なるコピーの例なので、コピー元が複数レコード存在する等々・・・
修正点はいっぱいあると思います。

投稿日時 - 2013-04-24 14:15:15

補足

わざわざ補足するほどでもないかもしれませんが、修正し忘れたので・・・
>SELECT文など一部変えています
元々SELECT文使われていないです。条件等変更しました。

投稿日時 - 2013-04-24 16:34:31

お礼

ご回答頂きまして、ありがとうございました。
非常に助かりました。

>1)rsClone での判別が正しいと思います。

安心しました。
rsCloneで氏名検索したのに、何故rsで判別?と悩んでしまいました。
クローンと複製元はカレントが同期するの?いやそうでもなさそうだし・・・と。


>  ただし、> 次行からの処理 ではないと思います。

rsClone で山田太郎さんがいたら(EOFでなかったら)、rsにAddNewして値を代入・・・
とIFの中の処理のことを「次行からの処理」と表現してしまったのですが、
使い方が良くなかったでしょうか。

あ、下の方で書いて頂いたコードを拝見しましたが、複製元からクローンにコピーをされていますね。
元は「クローンで検索して、複製元にコピーする」の例だったのですが、
(初心者へのCloneメソッドの説明のための例なのでおかしいのかもしれませんが)
そうではないと思われて「次行からの処理ではない」という意味でしたでしょうか。


>2)そのような話は聞いた事がありません。

実は某PCスクールのテキストに記載のあったプロシージャで
(転記NGとのことですのでSELECT文など一部変えています)
1.を疑問に思い講師に質問したところ、2.と言われたのです。
あまりに腑に落ちないテキストと講師の説明を信用出来ず、
実際にADOを使われている方のご意見をお聞きしたかったのです。
ありがとうございます。


>a)私なら、こう記述します

その方がわかりやすいですね!
参考にさせて頂きます。


>b)変数名に気をつけます
>ドット?ピリオド?( . )の、あり/なしで同じ綴りになるものは使いません。

非常に役立ちます!ありがとうございます。


>以下の ★ 部分の記述は間違いですよね
>> rs.Clone.Close ' ★

これは私の打ち間違いです。すみません。
これも変数名に気を付けると、間違いを防げそうに思います。


>参考になるかどうかわかりませんが、私が記述したとしたら以下の様な感じに

どういう構造にしたらわかりやすいかなど、参考になります。
オートナンバの部分も、スクールに行った時に打ち込んで試してみます。
ありがとうございました。

投稿日時 - 2013-04-24 16:23:56

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.5

#4です

> ここを見たのですが、見る場所、理解が間違っていますでしょうか・・・
> http://msdn.microsoft.com/ja-jp/library/cc376486.aspx

見てみました。
コード例からしてみると EOF = True 判別で良いみたいですね。
Access2007のヘルプでは、

> Recordset から、指定した条件を満たす行を検索します。必要に応じて、検索の方向、開始行、および開始行からのオフセットを指定できます。条件が一致すると、カレント行の位置は、検出されたレコードに設定されます。条件を満たす行がない場合は、Recordset の最後 (または最初) に設定されます。

という記述のほかに、メモとして

> レコードセットに対して Find メソッドを呼び出す場合で、レコードセット内の現在の位置が最後のレコードまたはファイルの最後 (EOF) の場合、何も検索されません。あらかじめ MoveFirst メソッドを呼び出して、現在の位置またはカーソルをレコードセットの先頭に設定する必要があります。

引数 SearchDirectionEnum の説明では、
> 一致するレコードが見つからない場合、レコード ポインタは EOF に移動します。

という記述もあるんですけどね。


言葉の遊びなのかもしれませんが、
・「Recordset の最後」
・「ファイルの最後 (EOF)」
使い分けている?表現を変えている?っていう事は、何かあるんじゃないですかね・・・
と疑ったわけです。

msdn での説明でも
> 条件に合うレコードを検索できない場合、前方 (EOF = True) に検索していたときは、現在のレコードが Recordset オブジェクトの一番後ろに置かれます。

っていう表現で、EOF = True 判別されているので、ヘルプ内の表現
「Recordset の最後」=「ファイルの最後 (EOF)」
と、解釈した方が良さそうですね。(#4のコード例は、いらないという事で・・・)

迷わせてしまって申し訳ありません。

投稿日時 - 2013-04-26 02:42:59

お礼

ありがとうございました。
色々悩ませてしまいましたが、結果的に最初の回答で問題ありませんでしたので
#1をベストアンサーにさせて頂きます。
テキストの誤字から始まりましたが、色々検索したりテストしたり、ご意見もお聞きし、
大変勉強になりました。

投稿日時 - 2013-04-26 09:51:35

ANo.4

#3です

たびたびの連投ですみません。

Find 後の EOF についてなんですが、私の環境では対象がなければ EOF = True となってました。
そういう動きを想定していたので、フムフム・・・ということでヘルプ等は参照してませんでした。
その中で、あの回答を目にし、改めてヘルプを読んでみると・・・
私の場合、動作が食い違っていた時にはヘルプ側を信じて見る事をします。
で、その動きを再現する様にいろいろとやってみます。
(今もですが)#3ではその時間がないので・・・で記述していました

私の場合、Find を使ったものは、あまり作ってないので、
今後、ヘルプ通りの動きになったとしても、さほど影響は無いのですが・・・

どうしても再現できなかった場合、どちらでも動く記述にしておきますか・・・
例えば、

Dim sS As String
Dim bNext As Boolean
・・・・・・
・・・・・・
sS = "山田 太郎"
rsC.Find "名簿 = '" & sS & "'"
bNext = True
If (rsC.EOF) Then
  bNext = False
Else
  If (rsC("名簿") <> sS) Then bNext = False
End If
If (bNext) Then
  ' 見つかった時の処理・・・

※ Find はあまり使った事がないです。
Find よりも Filter を使った絞り込みの方を良く使ってました。
Filter 後は、RecordCount でレコード数を知る事が出来るし・・・
(EOF を見た単純ループでクルクル処理できる・・・・等など)
Filter は Find より遅くなると思いますが、想定したデータ量で満足できる応答であれば・・・

※ 今もチョコチョコと再現を試みていますが難儀しています(Vista + 2007)
現状では、
・対象がなかったら EOF = True になるし、
・他の部分 AbsolutePosition は負値になるみたい

惑わせてすみません。
回答としては、ヘルプ(仕様?)を提示すべきだったんでしょうか・・・

※ #2で記述したものはそのまま生きると思います。

失礼しました。(識者の回答を待ってください)

投稿日時 - 2013-04-25 08:37:56

補足

あ、すみません。
DAOならNoMatchプロパティ・・・は当然のこととして、
ADOの時EOFで判断出来ない、というヘルプが存在しているということでしょうか?
ここを見たのですが、見る場所、理解が間違っていますでしょうか・・・
http://msdn.microsoft.com/ja-jp/library/cc376486.aspx

投稿日時 - 2013-04-25 22:09:12

お礼

MSのサイト見ましたが、EOFにならないのはDAOの場合でないでしょうか?
ADOの場合、#3のお礼に記載した通りのようです。

投稿日時 - 2013-04-25 21:56:03

ANo.3

#2です

連投すみません
1)について、私の勘違いもあって・・・件名は違いますが以下の#1さんの回答を参照ください。

Access 重複しないメールをテーブルに取り込む
http://okwave.jp/qa/q8056866.html

なので、Find は使わない方向が良いのかも・・・・

投稿日時 - 2013-04-24 22:35:56

お礼

引き続きありがとうございます。
テキストには、Findの検索方向が「adSearchForward」(規定値)の場合、
レコードが見つからなかったらEOFと書いてあるのですが。
動きも確認したような・・・次回スクールで再度確認します。

投稿日時 - 2013-04-25 21:48:53

ANo.2

#1です

試されるという事なので、補足等記述できるように回答しておきます。


> 実は某PCスクールのテキストに・・・・

であれば、まずはソコソコ動くものを・・・
で、徐々に環境/条件を変化させて・・・以前の記述ではエラーになるね・・・とか?

というストーリーも考えられます。


> 1.を疑問に思い講師に質問したところ、2.と言われたのです。

推測するに、たぶんですが
・ Find では必ず一致するものがある

これが前提であり、If Not rs.EOF Then としたのは、テーブルが空だったら・・・・
という事になるんでしょうか。

チェックがチグハグの様な気がしますが、
元々の上位記述で、対象のレコードがなければ実行しない・・・・
とかしていれば、それなりに動くとは思いますけど・・・
(とは言っても、私は私のスタイルで記述しますけど)


> rsのカレントがEOFだとAddNewでエラーになるから・・・

を素直に受け止めると、空のテーブルにレコードは追加できない・・・に、なると思います。
(やはり、何かストーリーがあるような気がします)


私が参考として提示したものは、rs を求める時にコピー元を求めており、
テーブルが空 / 対象がない 時には EOF が True になるので、まとめた判別が出来てます。
(コピー元があって、クローンを作るようにしています)
で、コピー元があったら、それは rs のカレント=先頭になっているので、
Clone して クローン側の Bookmark 操作云々を省く事が出来ると思います。
なので、Clone 側に AddNew する事を、私はよくやります。
また、Clone して作った rsC の初期の位置は先頭(???)
この辺は、ヘルプに記述があったような・・・で、確認してください。
(余談で、Filter は解除されたものになる?・・・とかも確認ください)

※ rs.Clone.Bookmark = rs.Bookmark も、変数名・・・・での記述ミスでしょうか


※ 次行 については、勘違いをしていました。
Find で求めた次の行(レコード)と・・・・失礼しました。

投稿日時 - 2013-04-24 21:46:48

お礼

引き続きありがとうございます。悩ませてしまってすみません
本日、わざわざ別の校舎の講師に話を聞きに行きました。
結果は
1.rs→rsClone にすべき
2.AddNewがEOFでエラーになるという話はない
※その講師も「エラーになる」と言った講師からそのように習ったそうで
 最初は「エラーになる」と言っていたのですが、
 どこのテキストにもヘルプにも載っていない、実際EOFでもエラーにならないことを見せ、
 確認してもらいました。

まず前提に、そのスクールのオリジナルテキストには誤字が多い、というのがあります。
通常は、テキストで説明された通り、基本に忠実なプロシージャが載っています。

テキストの解説には「対象のレコードがあった場合・・・」と書いてあって、
rsClobeでレコードを探してるのに If Not rs.EOF Then となっていることに
違和感を感じました。

>・Find では必ず一致するものがある
>これが前提であり、If Not rs.EOF Then としたのは、テーブルが空だったら・・・・
>という事になるんでしょうか。
>チェックがチグハグの様な気がしますが、

チグハグですよね。
「rsがEOFだとエラーになるから」と言われた後、
「対象レコードがあった場合、という条件になっていないですよね」と言ったら、
「必ず一致する前提で進んでいますね」と言われました。
If Not rs.EOF Then は
「通常Openしたら先頭レコードがカレントですがEOFのこともあるので」
と言われました。
「そうしたら、EOFの時にコピーできるように対応してないのは何故ですか」と聞いたら
「テキストの例ですので、実務とは違います」と言われました。
そんなチグハグをおかしいと思わない講師にこれ以上聞いても
納得行く回答はされないだろうとあきらめ、
こちらに相談させて頂き、違和感を感じるのは私だけではないはずと確信し、
他の校舎の講師をわざわざ訪ねてみました。
愚痴になってすみません。

>rs.Clone.Bookmark = rs.Bookmark も、変数名・・・・での記述ミスでしょうか
そうです!すみません。
これも直した上で、教えていただいたオートナンバーの処理も確認しました!
ちなみにですが、
「イメージしやすいようにBookmarkの処理を入れていますが、なくても結果は同じです。」
と書いてありました。
(それなくてもイメージ出来るのに、そんなことより誤字をなくして欲しい・・・)

投稿日時 - 2013-04-25 21:43:34

あなたにオススメの質問