Do Loop文の処理やNz関数で何をしているのかがわかりません


仕事でAccessを使っていると以下のようなVBAコードに遭遇しました。

-------

Do Until animaltable.EOF
countanimal = countanimal + 1
With animal_data(countanimal)
.animal_number = Nz(animaltable.Fields("animalvariation").Value,0)
End With
countanimal.MoveNext

-------

animaltableというのは、

Dim animaltable as DAO.Recordset

で定義されている変数で、動物の数を取得するためのレコードセット?らしいです。
animal_dataというのは、別のモジュールで「Public animal_data() As ANIMAL_INFO '動物の情報」と定義されておりました。

animalvariationというのは、別にanimalSQLという変数がありまして、その変数にはSQL文が格納されています。
そのanimalSQL文の中に「アニマルテーブル.animalの種類 as animalvariation」という文が出てくるので、おそらくアニマルテーブル内にある列「animalの種類」の別名をanimalvariationに設定しているのかと。

Withステートメントは、「オブジェクト名を省略できる」「オブジェクトへの参照回数が減り、実行速度が向上します」とあるので、animal_dataオブジェクトに対し、animal_numberという処理を行っていることはわかります。

回答の条件
  • 1人10回まで
  • 登録:
  • 終了:2019/11/27 00:10:11
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。
id:moon-fondu

ただ、断片的には判るものの、全体として…何をしているのかいまいちピンと把握できておりません。

Nz関数も、「animaltable.Fields("animalvariation").Value」という元の値を、「0」にしている、というのはわかります。

しかしanimaltableというレコードセット?を、.(ドット)でつなげた後のField?というのもよくわからず…Valueプロパティは、オブジェクトの属性(値)を付与する等、本に書かれている内容の一部はなんとなく判りますが、それらが組み合わさったときに何が行われているのかを把握しようとすると、頭の中で整理できなくなります…。

上記コードでは一体、どのような処理がされているのか、どなたか解説いただけますと助かります。

よろしくお願い致します。

ベストアンサー

id:a-kuma3 No.2

回答回数4973ベストアンサー獲得回数2154

ポイント500pt

ざっくり言うと、オブジェクトの配列から一つずつデータを取り出して処理している、と考えれば良いと思います。

Recordset からの取得とオブジェクトの配列では、以下の違いがあります。
オブジェクトの配列:ループを回す前に、全てデータを取り出してある
Recordset:ループを回している途中で、少しずつデータを取り出す

データベースからデータを取り出す場合には、何百万件というのを相手にすることもあるので、最初に全てデータを取り出すと、無駄なデータまで取り出すかもしれず、遅くなったり、メモリを無駄遣いする、という可能性があります。
なので、「少しずつデータを取り出す」のですけれど、ひとつずつ取り出すと、今度は一発でデータを取り出すよりも遅くなっちゃうので、「いくつかまとめて少しずつデータを取り出す」という戦法を取ります。

この「いくつかまとめて少しずつデータを取り出す」というのが、自分で全部書くと、地味に難易度が高いので、ひとつずつ取り出して処理をしているような書き方にできるのが Recordset です。

配列だと最初にデータの件数が分かっているので、ループも「何件」という終了判定になりますけど、少しずつ取り出す場合には、何喧嘩も分かってないので、終了判定が「データがなくなるまで」=「animaltable.EOF」となります(EOF : End Of File)。


もうひとつ、Nz関数の話。

データベースは、言ってみれば「表」のようなものですけれど、そのひとつのマスには「何も入っていない」という状態を作ることができます。
「何も指定しなければ数値のゼロが入っている」ではなくて「何も入っていない」です。
「何も入っていない」は数値ではないので、扱いが面倒なのですけれど、何も入っていないので、ファイルのサイズを節約できます。

それの扱いを簡単にするのが Nz関数です。
データを作るときに、特に入れる値がなければゼロを入れておく
 なのか
データを作るときに、特に入れる値がなければ、何も入ってないままにしておいて、取り出したときにゼロで扱う
 のどちらかです。

後者は、「何も入っていなければ」という判定を入れることになるのですが、それを簡略化してくれるのが Nz関数です。
Nz関数を使わなければ、

    n = 0
    If Not IsNull(animaltable.Fields("animalvariation")) Then
        n = animaltable.Fields("animalvariation").Value
    EndIf
    .animal_number = n

という感じになります(他のやりようも、いくつかあります)。
これを Nz関数を使うと、一行で書ける、ということです。

    .animal_number = Nz(animaltable.Fields("animalvariation").Value,0)
id:moon-fondu

ありがとうございます、Recordsetについて理解が深まりました!
Nz関数も、本よりも詳しい解説をしていただき、ありがとうございます!!
nullではない(If Not)時は、n = animaltable.Fields("animalvariation").Value ということで、animalvariation列の値を取得しまして。
それ以外の時は、 .animal_number = n すなわち0を入れるということですね。

それをNz関数なら一行で書ける…わかりました。

2019/11/21 22:18:31

その他の回答1件)

id:kaoato No.1

回答回数236ベストアンサー獲得回数86

ポイント500pt


animal_data()  :配列変数(構造体の配列)

配列を使用する (VBA) | Microsoft Docs
https://docs.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/using-arrays



animal_data().animal_number


animal_data()は配列だけど、たぶん構造体になっている。

その構造体のanimal_number という変数を上記で指定

構造体およびその他のプログラミング要素 (Visual Basic) | Microsoft Docs https://docs.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/language-features/data-types/structures-and-other-programming-elements




animaltable :レコードセット

クエリ(SQLで条件指定したクエリ)、レコードセット


Recordset オブジェクト (ADO) | Microsoft Docs https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/recordset-object-ado


animaltable.Fields("animalvariation").Value 


animaltableクエリ(レコードセット)の「animalvariation」フィールドの値



Fields コレクション | Microsoft Docs
https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/the-fields-collection

Field オブジェクト (Access デスクトップデータベースリファレンス) | Microsoft Docs
https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/the-field-object



EOF

最後のレコードを超えたかどうか

Recordset.EOF プロパティ (DAO) | Microsoft Docs https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/recordset-eof-property-dao



次のレコードに移動

Recordset.MoveNext メソッド (DAO) | Microsoft Docs https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/recordset-movenext-method-dao



Do Until文

Do...Loop ステートメント (VBA) | Microsoft Docs https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/doloop-statement






With ステートメント (VBA) | Microsoft Docs https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/with-statement


Do Until animaltable.EOF
   countanimal = countanimal + 1
  
  With animal_data(countanimal)
    .animal_number = Nz(animaltable.Fields("animalvariation").Value,0)
  End With

  'countanimal.MoveNext <=これ間違ってるのでは?
  animaltable.MoveNext  <=たぶん、これだと思う

Loop


上記から、withを取り除く


Do Until animaltable.EOF
  countanimal = countanimal + 1

  animal_data(countanimal).animal_number = Nz(animaltable.Fields("animalvariation").Value,0)

  animaltable.MoveNext  <=たぶん、これだと思う

Loop


こんな感じになる。


上記のソースをみると、

animal_dataという配列に、クエリ(レコードセット)のanimalvariationというフィールドの値を、代入している。


1レコード目

animal_data(1).animal_number  = 1レコード目のクエリ(レコードセット)のanimalvariationというフィールドの値


こんな感じで、レコード文、配列の構造体のanimal_number に値を代入している

これを、レコード数だけ、DOループで、配列変数(構造体)に代入している




===============



追記:

Nz 関数 - Access
https://support.office.com/ja-jp/article/nz-%E9%96%A2%E6%95%B0-8ef85549-cc9c-438b-860a-7fd9f4c69b6c

id:moon-fondu

FieldsコレクションのFieldオブジェクト、列に対応しているんですね。
リンクもありがとうございます。
1つ1つ読んで、勉強したいと思います。

構造体…これは骨が折れそうな内容ですね。
またこれについては質問するかもしれませんので、その際はよろしくお願いします<m(__)m>

RecordsetオブジェクトとRecordsetプロパティは区別して用いているようなので、この違いも違うようで…すみませんご紹介いただいたリンクを読んだのですが、いまいち理解できず…具体的に、RecordsetオブジェクトとRecordsetプロパティはどのように違うのでしょうか?

Recordsetオブジェクトはクエリの塊、Recordsetプロパティはクエリの処理内容、のような認識で問題ないでしょうか?

2019/11/21 22:18:06
id:kaoato

>RecordsetオブジェクトとRecordsetプロパティは区別して用いているようなので

ごめんなさい。間違ってました。

Recordsetオブジェクトです。リンク先も修正しました。

2019/11/21 23:33:32
id:a-kuma3 No.2

回答回数4973ベストアンサー獲得回数2154ここでベストアンサー

ポイント500pt

ざっくり言うと、オブジェクトの配列から一つずつデータを取り出して処理している、と考えれば良いと思います。

Recordset からの取得とオブジェクトの配列では、以下の違いがあります。
オブジェクトの配列:ループを回す前に、全てデータを取り出してある
Recordset:ループを回している途中で、少しずつデータを取り出す

データベースからデータを取り出す場合には、何百万件というのを相手にすることもあるので、最初に全てデータを取り出すと、無駄なデータまで取り出すかもしれず、遅くなったり、メモリを無駄遣いする、という可能性があります。
なので、「少しずつデータを取り出す」のですけれど、ひとつずつ取り出すと、今度は一発でデータを取り出すよりも遅くなっちゃうので、「いくつかまとめて少しずつデータを取り出す」という戦法を取ります。

この「いくつかまとめて少しずつデータを取り出す」というのが、自分で全部書くと、地味に難易度が高いので、ひとつずつ取り出して処理をしているような書き方にできるのが Recordset です。

配列だと最初にデータの件数が分かっているので、ループも「何件」という終了判定になりますけど、少しずつ取り出す場合には、何喧嘩も分かってないので、終了判定が「データがなくなるまで」=「animaltable.EOF」となります(EOF : End Of File)。


もうひとつ、Nz関数の話。

データベースは、言ってみれば「表」のようなものですけれど、そのひとつのマスには「何も入っていない」という状態を作ることができます。
「何も指定しなければ数値のゼロが入っている」ではなくて「何も入っていない」です。
「何も入っていない」は数値ではないので、扱いが面倒なのですけれど、何も入っていないので、ファイルのサイズを節約できます。

それの扱いを簡単にするのが Nz関数です。
データを作るときに、特に入れる値がなければゼロを入れておく
 なのか
データを作るときに、特に入れる値がなければ、何も入ってないままにしておいて、取り出したときにゼロで扱う
 のどちらかです。

後者は、「何も入っていなければ」という判定を入れることになるのですが、それを簡略化してくれるのが Nz関数です。
Nz関数を使わなければ、

    n = 0
    If Not IsNull(animaltable.Fields("animalvariation")) Then
        n = animaltable.Fields("animalvariation").Value
    EndIf
    .animal_number = n

という感じになります(他のやりようも、いくつかあります)。
これを Nz関数を使うと、一行で書ける、ということです。

    .animal_number = Nz(animaltable.Fields("animalvariation").Value,0)
id:moon-fondu

ありがとうございます、Recordsetについて理解が深まりました!
Nz関数も、本よりも詳しい解説をしていただき、ありがとうございます!!
nullではない(If Not)時は、n = animaltable.Fields("animalvariation").Value ということで、animalvariation列の値を取得しまして。
それ以外の時は、 .animal_number = n すなわち0を入れるということですね。

それをNz関数なら一行で書ける…わかりました。

2019/11/21 22:18:31
  • id:outofjis
    まずはデータベースの何たるかと、VBAを概略だけでも理解しないといけないのでは。


    やっている処理は至極単純で、「animaltable」という表(エクセルのシートみたいなものと考えてください)の、
    「animalvariation」という名前の列を上から順に参照しているだけです。
    恐らくは、animalvariationには、動物の種類の数が入っているのでしょう。

    ただ、なにも記載されていない場合には、「0」として扱いたいためNZ関数を使用しています。

    NZ関数は、値が入っていればその値を、何も入っていない(NULL)の場合は、この場合は0を返します。
    たとえば、10が入っていれば10を、0が入っていれば0を、何も入っていなければ0を返します。

    その結果を、「animal_data」という表の(これはデータベースではない表です)
    「animal_number」という項目に入れています。


    大したことなっていないけど、言葉にするとややこしいなぁ。
  • id:moon-fondu
    そうですよね。基礎がまだグラついている気がします。
    recordsetについては、a-kuma3さんの説明で、少しずつ取り出すという機能的な部分の特徴は判ったのですが。
    それがMicrosoftの公式説明にありますような、recordsetオブジェクト、recordsetプロパティ、という呼称になると…少し頭の整理が追い付かなくなります。
    プロパティは性質的なものに付けていたような…オブジェクトは、性質ではなくそのクエリそれ自体?と認識しております…。

    animal_data、データベースではない表…構造体だからですか…構造体は1度しっかり勉強しないといけませんね。
  • id:a-kuma3
    >それがMicrosoftの公式説明にありますような、recordsetオブジェクト、recordsetプロパティ、という呼称になると…少し頭の整理が追い付かなくなります。
    オブジェクトは、他のオブジェクトのプロパティになることがあります。

    AAAオブジェクトの bbb プロパティは BBBオブジェクトである。
    そういう場合があります。
    そういったケースでは、どちらを主体に文章が書かれているかで書きようが変わります。
    AAA を主体に書けば、BBB は AAA のプロパティです。
    AAA のことを考えずに BBB を主体に書けば、BBB はオブジェクトという呼び方で記述されます。

    質問に書かれているケースだと、Recordsetオブジェクトの Fieldsプロパティは、コレクション(Collection)のオブジェクトです。
  • id:moon-fondu
    そうなんですね。プロパティになったりオブジェクトになったりするとしても、主体となっているオブジェクトを意識していきたいと思います。
    Fieldsも、Recordsetに対してはプロパティ、Collectionに対してはオブジェクトになり。
    関係性で呼称や役割が変わるんですね。

    ありがとうございます<m(__)m>

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません