Powered By Blogger

暇がない。自力じゃ無理な人はこちらへ。

2014年11月23日日曜日

名前の定義の注意

シートに問題が生じたので、シートを丸ごとコピーして、そこでテスト・確認する。
なんて言うシチュエーションはよくあると思う。
しかし、そのシートに名前の定義されているエリアがある場合、ちょっと注意が必要だ。

1.シートをコピーしたとき名前の定義はどうコピーされるのか?

テストのため、Sheet1に範囲1という名前を定義して、範囲1の最大値を表示するような場合を考えて、これで説明することにする。

まずSheet1の名前の定義は下のようになっている。



次にSheet1をコピーしてSheet1(2)を作成。

この時の名前の定義は下のようになる。


これを見て明らかなように、”範囲1”という名前の定義が2つになっている。違いは、参照範囲のシート名。それと、最初に定義したものは範囲が”ブック”になっているが、シートコピー時にエクセルが自動的に作成した定義の方は範囲が”Sheet1(2)”となっているところだ。

そして、このSheet1(2)のMAX関数が使っている”範囲1”は新しいSheet1(2)の”範囲1”だ。

2.どんな問題が起きるのか?

実は上の例では問題は起こらない。

Sheet1を削除した場合に上のようにゴミが残るのと、”範囲1”が他のシートから参照できない程度だ。しかし、この他のシートから参照できないというのがまずい。

というのも、問題が起こるのは、”範囲1”を別シートから参照していた場合だ。
例えば、範囲1がSheet1に、MAX関数がSheet2にあって、その後Sheet1をコピーしてSheet1(2)を作成した場合。

はたして、このMAX関数が参照している”範囲1”はどっちの”範囲1”だろうか?
当然、元からあったSheet1の”範囲1”の方なのだが、見た目ではわからないし、以外と盲点だ。

そして、コピーしたSheet1(2)は念のためのバックアップで、変更修正自体は元のSheet1の方に対して行う場合、例えば範囲1の5を10に変更するような場合は、

とMAX関数は元のSheet1の範囲1を参照しているので問題は起こらない(この時は・・・というのも、何か問題が発生して、バックアップのSheet1(2)を使う必要になったときには、後述する場合と同じことだから。)

しかし、元のSheet1をバックアップとして残し、Sheet1(2)の方に変更修正を加える場合には問題が起こる。


と、意図した結果にならない(まあ、間違って意図しているわけだが)。

しかし、この場合は、すぐに何かおかしいと気が付くので、まだ被害は少ない。

問題なのは、一目ではわからないような変更を行った場合だ。例えばMAXではなくてMIN関数だった場合、結果は変わらないので、問題に気づきにくい。

そして、Sheet1(2)の変更を確定して、オリジナルのSheet1を破棄して、Sheet1(2)をSheet1と変更してしまった場合。これは最悪だ。

当たり前と言えばそうだが、MAX関数で使用していた”範囲1”は元のSheet1と共に消えてなくなっているので、下のようにREFエラーとなってしまう。
しかも、新Sheet1で定義されている(コピー時にエクセルが自動的に定義した)”範囲1”の範囲はSheet1となっていて、MAX関数が記述してあるSheet2では使えない。


ならば、新Sheet1の”範囲1”の範囲をブックにしてやれば・・・・
しかし、これが変更不可ときている。


こうなると打つ手なし。もはや、”範囲1”を一旦削除して、新たに定義し直すしかないだろう。
トホホ・・・

3.対策はあるのか?


というわけで、名前の定義を使用しているシートをコピーする場合、というかシートのコピーはするな。
コピーするならファイルごとコピーする。
これが正解のような気がする。

そう言ってしまうと身もふたもないので、ちょっと考えてみる。
・・・・こういうのはどうだろう?


とういう具合に名前の定義の参照範囲をINDIRECT関数で指定するというのはどうだろうか。

この状態でSheet1をコピーして、Sheet2のB3の内容を”Sheet1 (2)!$A$1:$A$5”としてみたところ、


あれ?
どうやらINDIRECT関数がエラーを吐いているようだ。
どうも、スペースとカッコがまずいみたいなので、シート名を変えることにした。

よし。動作しているようだ。
(Sheet1とSheet12を区別するため、Sheet12では最大値を10と変えてある)

名前の定義を直接修正しなくても良い分、うん。こっちの方がいいな。
どっちにしろ、こういうことが起こることを考慮しておく必要はあるな。


2014年11月20日木曜日

優先順位に従って、一定量になるまで累計をとる。

事例:

ある物が格納されている機器が数台あるのだが、そこから毎朝一定数の物を抜き出して別の箇所に移動させないといけない。
一定数というのは、1台につき何個じゃなくて全体で何個ということだ。
例えば、


上のような機器と格納数で、全体で300個抜かないとならない。
というようなシチュエーションだ。


”物”についての具体的な話は問題があるので、今回は”物”ということで。

単純に必要な数量に達するまで順番に抜いていけば良いのだが、どうも制限があるらしい。それは、

     ”ある機器からは、なるべくなら抜きたくない。とっておきたい”

なら、機器に優先順位を付けておけばだいじょうぶ。

だが、

     ”物”を抜きたくない機器は、店舗ごとに違う

らしい。

つまり、優先順位を変えられるようにしておく必要があるということだ。

ということで、今回のネタは、

「一定の数値になるまで、いくつかのセルの値を順番に足していく。ただし、この順番は設定で変えられるようにする。」

ということだな。

やり方としては、
①まず優先順位の順番に並べ替える
②次に行ごと累計を必要数になるまで計算する。
という2段階にわけて考えよう。

ステップ1:行ごと累計を必要数になるまで計算する。

まずは簡単そうな②から。
とは言っても、1つのセル内に数式を詰め込むと複雑になりそうなので、
 1.単純な累計を求める列を追加
 2.それに対して一定数に達しているか判断して、抜き取り数を決定。
と2段構えでいくことにしよう。

結果はこうだ。



1.単純な累計を求める列を追加

D列の単純な累計については説明する必要はないだろう。

とは言ったものの、上のように単に足していく方法とSUM関数の範囲を変えていく方法がある。
SUM関数のメリットは最初の行に
 =SUM($C$3:C3)
と入力して、後の行はコピーで行ける点。まあ、足し算でいく場合も2行目からはコピーで行けるのでメリットと言えるかどうか・・・

それに対してデメリットは、
行数が増えていくと、SUM関数の足し算の計算量がNx(N-1)/2となる。オーダー表記で書くと
となる。
一方、ただの足し算はN-1回なので、

どっちにしろ5行程度なら影響ないな。お好みで。

2.必要数に達しているか判断して、抜き取り数を決定。

抜き取り数を決めているE列について説明すると、
まずは最初の行、E3の式について。
 
 ”格納数が必要数より多かったら、必要数分抜き取る。必要数以下だったら、全部抜き取る”

ということ。


それ以降の行について。

”累計数が必要数より多かったら、必要数に足りない分を抜き取る。ただし、すでに前の行までに必要数に達していたら、何も抜き取らない。
累計数が必要数以下だったら、全部抜き取る”


言葉で説明するとこうなるが、分かりにくいな。

というわけで図にしてみた。




これで多少すっきりしたかな。

参考までに累計を別列で計算するのではなく、式の中で計算する場合の例も挙げておく。




ここでは累計の計算はSUM関数を使っている。なぜならセルの足し算だと、2行目まで違う式を入れないとダメなため。全体を把握しづらい。
どっちにしろ、ちょっと面倒そうな式だな。
やっぱり累計は分けた方がよさそうだ。

問題があるとすれば、実際に機器を操作して”物”を抜き取る作業者に余計な情報を渡すことのになるくらいか。
ステップ1の結果の表

これでOK。

さて、やっとステップ2だ。

ステップ2:優先順位の順番に並べ替える

単純に機器名と格納数が入っているセルを並べる順番を変えれば、対応はできる。
そして、店舗ごとにエクセルのファイルの種類を変えれば。

しかしそれだと管理が面倒だし、第一面白くない。

1.機器名を直接指定する場合

さて、どうするか。
機器の優先順位を設定する別表が必要だろうな。
こんな感じの。
機器名に直接入力する



ちなみに、ダブったところの色を変えるのは条件付き書式で対応。
この表の優先順位に対応する箇所に機器名を直接入力することにしよう。


更に、それぞれの機器の格納数も隣の機器名に従って可変にする必要があるので、これも別表にしておく。
格納数は直接入力

ここの格納数には直接、数値を入力する。
なんか、こう、表を分けていく過程はデータベースの正規化にちょっと似てるな。
そしてこれらの表から、おきまりのMATCHとINDEXの組み合わせで問題なく目的は達成できる。

あと、優先順位に何も設定されていないときなど、MATCH関数がN/Aを吐いたときのために、IF文とISNAを追加すると下のような式になる。








*上の式には配列数式を使っているが、普通の式を下の行にコピーしていっても同じ。
ただ、配列数式の方が、処理内容が解りやすい。と思う。

2.機器名をリストから選択する場合

だが、ちょっと待て。
機器名を直接入力というのは、イマイチかな。
AとかBなんて簡単な名前は稀だろう。番号を決めてそれで入力という手もあるが、もうちょっとスマートに行きたいな。

やはり入力規則を使ってリストから選択できるようにした方がいいかも。
問題になりそうなのは、すでに優先順位を設定してある機器名をリストから排除していくやり方だろう。

また、機器リストを自動的に順番に並べるためには機器の大小関係が必要なので、機器番号を導入する。

この上の表のセルには数式ではなく値を直接入力する。
これをベースにして、すでに優先順位に設定してある機器名を排除していく。



1.Q列の式では、機器番号のリストから、すでに設定されている機器の番号を""で置き換える。





2.R列の式では、SMALL関数を使ってQ列を小さい順に並べ替える。



3.S列の式では、並べ替え後の番号に対応する機器名を表示する。




ちなみに、上のQ~Sの式は1列にまとめることもできる。

しかし、複雑になりすぎるので、分けておいた方が解りやすいだろう。

さて、もう少しだ。
入力規則の範囲指定に使うために、S3~S7の範囲で、エラーが発生していない範囲を、”選択可能機器リスト”という名前として定義する。
(可変範囲はOFFSET関数を、行数のカウントにはCOUNT関数を使用している。)


そしてセルK3~K7に次のように入力規則を設定する。


これで完了だ。

最後に動作テストをしてみよう。
まず、優先順位に何も設定されていない状態。

A~Eすべての機器が選択リストに表示されている。

次に、優先順位1位にAを設定したあと、最も優先順位が低い機器を設定するというシチュエーションを考える。


ちゃんと選択リストにはAを除いた機器が表示されてる。

そして、優先順位最下位をDとしたあとに、優先順位2位の機器を設定する場合。


A,Dを除いたリストが表示される。

よさげな感じだ。


ついでに入力時とエラー時にメッセージを表示するようにしてみた。




テストのために、直に機器名を入力してみた。
正しい機器名だが、エラーではじかれるな。
しかし、これは致し方ないか。
ちょっと気に入らないが。

というわけで、今回はこれで終了。
あまり纏まっていないが・・・