クエリでのRnd関数による抽選

 多数あるレコードのうちランダムに一定数のものを抽出するという抽選を行う場合、ExcelですとRAND関数を使って各レコードに異なるランダムな値を与え、その値の大小で抽選結果を決定するというのが一般的です。
 Accessの場合、テーブルではRnd関数を使えませんし、クエリでも関数を「Rnd()」などと記述すると全てのレコードで同じ値になってしまうためうまくいきません。そこで対応例を示します。

例1:クエリのRnd関数で完結させる例

 元のテーブルはそのままに、クエリでのRnd関数だけで抽選を行う例です。ただし、レコードごとに異なる値を持つ数値型の(あるいは数値とみなせる)フィールド(列)が存在することが必要です。

 例として、このような数値を主キーとするテーブルがあるものとします(主キーである「ID」はプラスの値をとるものとします)。


 このテーブルをもとにしたクエリを作り、レコードごとに異なるランダムな値を与えるため次のような列を置きます。

抽選値:Rnd(-[ID])

 IDの値はプラスですので、-[ID]は常にマイナスの値となります。Rnd関数の引数の値がマイナスの場合、その引数をシードとして生成される一定の値を返すことから、Rnd(-[ID])は同じIDの値(同じレコード)に対しては常に同じ値を返します。しかし、異なるIDの値(異なるレコード)に対しては異なる値を返し、その大小関係はまちまちですので、この値を抽選に用いることができるというわけです。


 このクエリをデータシートビューでみてみると、レコードごとに異なる値が発生し、それを基準として並べ替えられていることがわかります。これにより当選者を確定させることができます。


 ただし、上記の例ですとRnd関数が返している値に似通った部分が多いのが気になります。Rnd関数の詳しいアルゴリズムはよくわかりませんが、いずれ引数が単純すぎると適切な抽選にならない(ただの等差数列となる、など)という可能性は否定できません。
 そこで、蛇足ではありますが別の例として次のようにしてみます。

抽選値: Rnd(-2.5*Abs(Sin([ID])))

もちろん数値や関数は適宜変えても問題ありません。
 いずれ、Rnd関数の引数の値がマイナスとなるようにしてください。


 データシートビューをみてみると、先ほどの結果よりはランダムらしくなっているものと思います。数学的にどの程度もっともらしい乱数になっているかはわかりませんが、厳密さを問われるケースでない限り特に問題はないものと思います。

 なお、注意点として、同じIDを持つ対象者に対し繰り返し抽選する場合は引数を変えないと毎回同じ結果となりますので、Rnd(-[ID]-1)とかRnd(-[ID]/2)にするなど、とにかく毎回変える必要があります。
 また、日付や時刻に応じて毎回違う結果にしたい、というときは値(抽選値)を算出する式にTimer関数やDay関数を含めるのも一つの方法ですが、その都度結果変わりますので結果を保持する必要がある場合はその方法を考慮する必要があります(テーブル作成クエリ、エクスポートなど)。


例2:更新クエリまたはテーブル作成クエリによりテーブルにランダムな値を書き込む例

 Rnd関数により得られた値をテーブルに書き込むやり方です。元のテーブルに書き込むのなら更新クエリを、抽選結果を記録する別にテーブルを作るのならテーブル作成クエリを利用しますが、以下では更新クエリの例を示します。
 なお、この例では数値型の列が存在しなくとも利用できる方法を示します。そうする(できる)理由については後述します。

 例として、このようなテーブルがあるものとします。


 抽選用の値を記録するため、抽選値という名称の数値型の列を追加しておきます。もちろん値の入力は必要ありません。


 

 次に更新クエリを作成します。列として抽選値を選択し、「レコードの更新」の行に次のように記述します。

Rnd(Abs(Asc([氏名])))

 例1と違ってAbs(Asc([氏名]))はプラスの値となります。しかもAsc関数は頭文字の文字コードを返すものですので、氏名の頭文字が同じであれば同じ値となります。しかし、Rnd関数の引数がプラスの場合、その引数が常に同じものであってもそのたびに異なるランダムな数値が返されますので、この値を抽選に用いることができるというわけです。しかし、単に引数がプラスならよいというものではなく、Rnd(1)などと記述するとなぜか常に同じ値が返ってきてしまいます。レコードごとに異なる値を得るためには、テーブルのいずれかの列を読み込んだ上でRnd関数にプラスの引数として与えるというつくりにする必要があります。そこで上記のような関数としています(もちろん条件を満たしさえすれば別の関数を使っても機能します)。
 さて、なぜ例1でRnd関数の引数の値をプラスにしなかったかというと、Rnd関数の引数の値がプラスの場合、データシートビューでレコードを選択するたびに、あるいはクエリを開きなおしたりAccessのウインドウを広げたりするたびに値がコロコロ変わってしまい、抽選結果の保持が難しくなるためです(結果について承認を得た後に当選者に通知しようと思ったら当選者そのものが変わってしまった、という状況が起こります。また、クエリに基づくレポートを作ったときにクエリと異なる抽選結果を表示してしまうなどの不具合が起こります)。この例では抽選値をテーブルに書き込んでしまいますので値の変化を気にする必要がありません。

 更新クエリを実行し、テーブルを開いた結果です。この抽選値に基づき当選者を確定させることができます。