[[20120502143409]] 『自働値を検索する』(寧々) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『自働値を検索する』(寧々)

今現在値の検索を関数でしていますが、
データーが多いためすごく重くなっています。
それをVBAに変更できないか投稿しました。

名簿シートのG列に住所を入力後
郵便番号と地区が自働表示できるようにしたいです。

郵便番号と地区の値は郵便番号シートから引っ張ります。
何か方法ありませんか?ご指導よろしくお願いします。

名簿シート

	[A]	[B]	[C]	[D]	[E]	[F}	[G]
[1]		地区				郵便番号	番地
[2]		西				123−4567	西新宿1234
		東				123−4568	澁谷2345
		西				123−4569	青山12−46アパート
		南				123−4570	南沖縄1234
		北				123−4571	北海道1234

郵便番号シート

	[A]	[B]	[C]				
[1]	字名	郵便番号3	地区				
[2]	西新宿	123−4567	西				
[3]	澁谷	123−4568	東				
[4]	青山	123−4569	西				
[5]	南沖縄	123−4570	南				
[6]	北海道	123−4571	北				


 >今現在値の検索を関数でしています
 どのセルにどんな関数を設定しているのか? 記述してください。

 >データーが多いためすごく重く
 因みに名簿シート及び、郵便番号シートのデータは、それぞれ何行ぐらいですか?

 VBAでやるとなると、おそらくWorksheetのChangeイベントを活用する事になりそうですが、
 上記一行で何を意味するのか わかりますか?

 もし、分からないなら、このWorksheetのイベントは? Changeイベントは?
 等々から調べてみてください。調べて結果、分からない事柄をピンポイントで質問してください。

 WorksheetのChangeイベントを活用するの意味なんて、当然分かります ということなら、
 この中身のコードの問題になりますから、現在の関数の内容が大きなヒントになるかと思います。

 ichinose


 EXCEL のバージョンがありませんが、単に関数の計算が重いという事でしたら、
 計算後に関数を値に置き換えてはどうでしょうか。

 操作がいちいち面倒な場合は、2003 でしたらこんな方法でもできます。
http://takashixxx.blog88.fc2.com/blog-entry-2.html
 2007、2010 でしたらクイックアクセスツールバーが、2010 でしたら ユーザ定義タブも
 同様のことができます。

 VBA での処理であれば、まずは現在使用している数式を提示されてはどうでしょうか。
 (Mook)

 ichinose さん (Mook) さん 
 ありがとうございます。WorksheetのChangeイベントもためしてみました。 
 VBAについていろいろ調べました。
 VBA関数て言う物もあるんですね^^

 しかししょっぱなから躓いてしまいました。
 西新宿1234 の場合 → 「西新宿」だけをとりだしす方法が分からずじまいです。><
 5日間やってまだ4行です T0T
  Private Sub Worksheet_Change(ByVal Target As Range)
     If Target.Column <> 7 Then Exit Sub
     For Each c In Target
   MyRs = c.Offset(, 5).End(xlUp).Row

 数式です。以前教えていただきました。^^
 IF(G2 = "","",VLOOKUP(LEFT($G2,MATCH(0,INDEX(0/(MID($G2,ROW($1:$1000),1)+1),),0)-1),郵便番号!$A$1:$B$99,2,0))

 寧々

 >西新宿1234 の場合 → 「西新宿」だけをとりだしす方法が分からずじまいです。
 提示されえた数式と同じ規則で取り出すなら、

 Option Explicit
 Option Compare Text   'これ大事
 Private Sub Worksheet_Change(ByVal Target As Range)
    Dim rng As Range
    Dim crng As Range
    Set rng = Application.Intersect(Target, Range("g2:g" & Rows.Count))
    If Not rng Is Nothing Then
       For Each crng In rng
          MsgBox get_to_num(crng)
       Next
    End If
 End Sub
 Function get_to_num(ByVal rng As Range) As String
    Dim tstr As String
    Dim g0 As Long
    tstr = rng.Value
    get_to_num = ""
    For g0 = 1 To Len(tstr)
       If Mid(tstr, g0, 1) Like "[0-9]" Then
          Exit For
       End If
    Next
    If g0 > 1 Then get_to_num = Left(tstr, g0 - 1)
 End Function

 こんな感じで取り出せます。

 西新宿1234  → 「西新宿」が取り出せれば、

 後は、VBA内で Vlookupを使うには、どうすればよいかです。

 私は、Application.VlookUp  という書式で使うことが多いですが、
 これも調べえ見てください

 ichinose

 ichinoseさんありがとうございます。
 Application.VlookUp も調べてみました^^
 ただ頂いたコード中の検索値が分からないですが、、、、

 Function get_to_num ていうのは 
 西新宿1234 の場合 → 「西新宿」だけを取り出す部分になりますか?

 Private Sub Worksheet_Change
 は入力時のアクションを起こすの意味合いでよろしいでしょうか?

 そうしますと。
 get_to_num = 郵便シートの 字名 になりますか?

 寧々

郵便番号シートでApplication.VlookUpやってみましたが、
TESTは問題なく動きましたが、

 Sub test()
  Dim myR
  Dim myR2
  Dim MR
    On Error GoTo ErrorHandler
        With Worksheets("郵便番号")
            'MR = .Range("A2", .Range("A" & .Rows.Count).End(xlUp))
            myR = Application.WorksheetFunction.VLookup(.Range("D1"), .Range("A:C"), 3, False)
            myR2 = Application.WorksheetFunction.VLookup(.Range("D1"), .Range("A:C"), 2, False)
            .Range("E1").Value = myR
            .Range("F1").Value = myR2
        End With
Exit Sub
ErrorHandler:
    Worksheets("郵便番号").Range("E1").Value = "該当無し"
    Worksheets("郵便番号").Range("F1").Value = "該当無し"
End Sub

TEST を Private Sub Worksheet_Change くみいれると全く動かないです。
「引数は省略できません」とエラーがでてきました。
全く見当違いで使用していますが?

 Private Sub Worksheet_Change(ByVal Target As Range)
    Dim rng As Range
    Dim crng As Range
    Dim MR

    Set rng = Application.Intersect(Target, Range("g2:g" & Rows.Count))

    With Worksheets("名簿")
       MR = .Range("A2", .Range("A" & .Rows.Count).End(xlUp))
        If get_to_num = MR Then
            .Range(MR & "B").Value = Application.WorksheetFunction.VLookup(get_to_num, Worksheets("郵便番号").Range("A:C"), 3, False)
            .Range(MR & "G").Value = Application.WorksheetFunction.VLookup(get_to_num, Worksheets("郵便番号").Range("A:C"), 2, False)

    End If
    End With
 End Sub

 寧々

 >ただ頂いたコード中の検索値が分からないですが、、、、
 投稿したコードをもう少しだけ説明します。

 投稿コードは、名簿というシートのモジュールに記述します。
 Worksheet_Change イベントプロシジャーは、セルにデータが入力されると、
 実行されます。入力されたセルが何なのか?は、Targetというパラメータが
 教えてくれます。

 但し、このコード内では
    Set rng = Application.Intersect(Target, Range("g2:g" & Rows.Count))
    If Not rng Is Nothing Then
 このIF文の分岐でG列(正確には、G1は除く)に入力された場合だけ、
 処理されるようになっています。
 そのキーとなるのが
 Application.Intersectメソッドです。このメソッドの意味がわからなければ、
 次行のIF文でどんな場合だけ処理されるのかがきちんと理解できないはずです。
 Application.Intersectメソッドをよく調べてください。
 このChangeイベントプロシジャー内では、よく使われるメソッドですから、
 これは押さえておいてください。
 入力されたG列のセル(rng)に対して

      For Each crng In rng
          MsgBox get_to_num(crng)
      Next
 というように繰り返し処理をしているのは、
 Excelシートというのは、一度に複数のセルに入力することが可能になっているからです。
 つまり、複数のG列のセルに同時に入力された場合も考慮して、それぞれのセルでも処理される
 ようにFor文を使っています。
 では、このFor文内の
    MsgBox get_to_num(crng)  ‘のget_to_num関数とは?

 試しに
 名簿 シートのセルG2に
 中央1598
 と入力してみてください
 中央 がメッセージ表示されますよね?

 続いて、G3に
 セクシャルバイオレットナンバー1
 と入力してみてください
 セクシャルバイオレットナンバーとメッセージ表示されますよね?
 つまり、G列に入力された文字列を最初から1文字ずつ調べて、数字を検出したら、
 その手前までの文字列を取得し、表示しているのです。

 よって、「指定されたセルの入力文字列を最初から1文字ずつ調べて、数字を検出したら、
 その手前までの文字列を取得する」、これがファンクションプロシジャー  get_to_numの
 機能ということになります。
 get_to_num(crng) で得られた文字列こそが 検索値ということになります。

 よって、get_to_num(crng)で得られた文字列で郵便番号シートA:C列をVlookupで検索すれば、
 該当する郵便番号及び、地区が取得できることになります。

 ということから、コードをちょっとだけ見てみると、

If get_to_num = MR Then
  get_to_num は、引数が必須です(Rangeオブジェクト)から、エラーになりますよね。

 以上です。再考してみてください。

 Application.WorksheetFunction.VLookupは、私が普段使う書式とは違いますが、せっかくですから
 これでいきましょう。

 ichinose


ichinoseさんありがとうございます。

Application.Intersectはあるセルが、あるセル範囲内にあるかどうかを判定する
ことですね^^なんとなくイメージはわきました。
http://officetanaka.net/excel/vba/tips/tips118.htm

>For Each crng In rng
> MsgBox get_to_num(crng)
> Next
はメッセージBOXでてます。

>get_to_num(crng)
も分かりました。

Private Sub Worksheet_Change(ByVal Target As Range)

    Dim rng As Range
    Dim crng As Range
    Dim MR
    Dim yubin As Worksheet

    Set rng = Application.Intersect(Target, Range("g2:g" & Rows.Count))
    Set yubin = Worksheets("郵便番号")

    With Sheets("名簿")
       MR = .Range("A2", .Range("A" & .Rows.Count).End(xlUp))
    If Not rng Is Nothing Then
       'For Each crng In rng
           .Range(MR & "G").Value = Application.WorksheetFunction.VLookup(get_to_num(crng), yubin.Range("A:C"), 2, False)
           .Offset(, 1).Value = Application.WorksheetFunction.VLookup(get_to_num(crng), yubin.Range("A:C"), 3, False)
       'Next
    End If

    End With

 End Sub   
オブジェクト変数とWITH変数ブロックが設定されていません
とエラーが出ました。いろいろ組み換えましたが、分かりませんでした。^^;;
いったい何のヘマをやらかしたんでしょか?

 Function get_to_num(ByVal rng As Range) As String
    Dim tstr As String
    Dim g0 As Long
    tstr = rng.Value ’★エラー
    get_to_num = ""
    For g0 = 1 To Len(tstr)
       If Mid(tstr, g0, 1) Like "[0-9]" Then
          Exit For
       End If
    Next
    If g0 > 1 Then get_to_num = Left(tstr, g0 - 1)
 End Function

寧々


 >Application.Intersectはあるセルが、あるセル範囲内にあるかどうかを判定する
 田中さんのサイトは、「あるセルが、あるセル範囲内にあるかどうかを判定する」場合に
 Application.Intersectを使うと便利だよ という内容でApplication.Intersectの機能は、
 「複数のセル範囲の共有セル範囲を表す Range オブジェクトを返します。」というものです。

 Sub smp1()
    MsgBox Application.Intersect(Range("a1:c3"), Range("a1:e2"), Range("a1:b2")).Address
 End Sub

 このコードの結果を理解してみてください。

 提示されたコードについては・・・・、
 あれれえ(コナン風)、サンプルコードで提示したFor〜Next文が消えてるよねえ・・・。
 まっ、良いでしょう。For文も無しで行きましょう。

 Option Explicit
 Option Compare Text    ‘これは、忘れない事
 Private Sub Worksheet_Change(ByVal Target As Range)
    Dim rng As Range
   'Dim crng As Range   ‘For文なしなので、とりあえず要らない
   'Dim MR
    Dim yubin As Worksheet

    Set yubin = Worksheets("郵便番号")
    Set rng = Application.Intersect(Target, Range("g2:g" & Rows.Count))
    'これで G列と入力セルの共有セルを返すのですよね?

    Set yubin = Worksheets("郵便番号")

    With Sheets("名簿")
       'MR = .Range("A2", .Range("A" & .Rows.Count).End(xlUp))

    '  If Not crng Is Nothing Then   crngは、上で使われていないですよ!!
       If Not rng Is Nothing Then   'G列のどこかのセルが入力されていれば?
          Application.EnableEvents = False   'イベントの2重起動の防止
          .Range("b" & rng.Row).Value = ""
          .Range("b" & rng.Row).Value = Application.WorksheetFunction.VLookup(get_to_num(rng), yubin.Range("A:C"), 3, False)
          .Range("f" & rng.Row).Value = ""
          .Range("f" & rng.Row).Value = Application.WorksheetFunction.VLookup(get_to_num(rng), yubin.Range("A:C"), 2, False)
          Application.EnableEvents = True   'イベントの2重起動の防止解除
       End If
    End With
 End Sub

 これで一見すると作動しているように見えます。
 名簿シートのG列に番地を入力してみてください。

 が、完成ではありませんよ!!エラー処理がされていませんから・・・。
 エラーは、 On Error を使います。

 一連の問答で感じたことは、やはり、もう少し基本は押さえられた方がよいですよ!!
 HELPを参照してください。
 それから、基本のステートメントは、学習してくださいね!!

 ichinose


 ichinoseさんありがとうございます。
 VBAを理解する努力はしていますが(T0T) これが難問であります。

 For〜Next文とってしまってすみません><
 処理とのかみあいかたが、わからずにとってしまいました。もうしわけないです。

 エラーを調べてやってみましたが、
http://officetanaka.net/excel/vba/tips/tips104.htm
 どうですか?
On Error GoTo 0を使うことによってイベントが中止にならなかったので。
本当はコメントも入れたいのですが、できませんでしたので断念です。

Private Sub Worksheet_Change(ByVal Target As Range)

    Dim rng As Range
    Dim crng As Range
    Dim yubin As Worksheet
    Dim ObjRange As Range

    Set yubin = Worksheets("郵便番号")
    Set rng = Application.Intersect(Target, Range("g2:g" & Rows.Count))
    '複数のセル範囲の共有セル範囲を表す Range オブジェクトを返します
    Set yubin = Worksheets("郵便番号")

    On Error Resume Next

    With Sheets("名簿")
       If Not rng Is Nothing Then   'G列のどこかのセルが入力されていれば?

          Application.EnableEvents = False   'イベントの2重起動の防止
          .Range("b" & rng.Row).Value = ""
          .Range("b" & rng.Row).Value = Application.WorksheetFunction.VLookup(get_to_num(rng), yubin.Range("A:C"), 3, False)
          .Range("f" & rng.Row).Value = ""
          .Range("f" & rng.Row).Value = Application.WorksheetFunction.VLookup(get_to_num(rng), yubin.Range("A:C"), 2, False)
          Application.EnableEvents = True   'イベントの2重起動の防止解除

       End If
    End With
Exit Sub

On Error GoTo 0

 End Sub

寧々


 >VBAを理解する努力はしていますが(T0T) これが難問であります。
 この問題、簡単ではありませんよ!!

 >処理とのかみあいかたが、わからずにとってしまいました。
 これについては、後回しにしましょう。

 > On Error GoTo 0を使うことによってイベントが中止にならなかったので
 On Error GoTo 0は、イベントを中止にするためのステートメントではありませんよ!!

 Sub test()
    Dim a As Long
    Dim b As Long
    Dim c As Long
    a = 5
    b = 0
    On Error Resume Next
    '↑実行時エラーが発生してもプログラムを中断せず、エラーが発生したステートメントの次のステートメントから実行を継続します。
    c = a / b    'エラーにならない
    If Err.Number <> 0 Then MsgBox Err.Description
    MsgBox "実行時エラーでプログラムは、中断していません"
    On Error GoTo 0  'エラー処理を無効にします
    c = a / b   'エラーになる 'ここから、実行時エラーでプログラムが止まる
 End Sub

 新規ブックの標準モジュールに上記のコードをコピーして実行してみてください。
 同じ   c = a / b というコードがエラーにならない場合とエラーになり、
 プログラムが中断する場合があることを
 確認し、その理由に納得してください。
 これがOn Error GoTo 0 の機能です。

 ポイントは、いいかげんにOn Error Resume Nextステートメントを設定するのではなく、
 起こりうる可能性がある実行時エラーを想定し、そこのエラーを拾うために
 On Error Resume Nextステートメント設定するということです。

 今回の場合、割り算の計算でB=0でエラーになる事を想定し、その直前に
 On Error Resume Nextステートメントを記述しています。

 では、本題コードに戻れば、実行時エラーが起こる箇所は、

 > Range("b" & rng.Row).Value = Application.WorksheetFunction.VLookup(get_to_num(rng), yubin.Range("A:C"), 3, False)
 上記の検索で指定された字名が郵便シートのA列に存在しないとエラーが発生します。
 よって、その直前にOn Error Resume Nextステートメントを設定し、
 ここでエラー処理を施します。
 Private Sub Worksheet_Change(ByVal Target As Range)
    Dim rng As Range
    Dim crng As Range
    Dim yubin As Worksheet
    Dim ObjRange As Range
    Set yubin = Worksheets("郵便番号")
    Set rng = Application.Intersect(Target, Range("g2:g" & Rows.Count))
    '複数のセル範囲の共有セル範囲を表す Range オブジェクトを返します
    Set yubin = Worksheets("郵便番号")
    With Sheets("名簿")
       If Not rng Is Nothing Then   'G列のどこかのセルが入力されていれば?
          On Error Resume Next
          Application.EnableEvents = False   'イベントの2重起動の防止
          .Range("b" & rng.Row).Value = ""
          .Range("f" & rng.Row).Value = ""
          .Range("b" & rng.Row).Value = Application.WorksheetFunction.VLookup(get_to_num(rng), yubin.Range("A:C"), 3, False)
          .Range("f" & rng.Row).Value = Application.WorksheetFunction.VLookup(get_to_num(rng), yubin.Range("A:C"), 2, False)
          If Err.Number <> 0 Then MsgBox "指定住所は、見つかりませんでした"  'エラー時のメッセージ
          Application.EnableEvents = True   'イベントの2重起動の防止解除
           On Error GoTo 0
       End If
    End With

 End Sub

 この先、プログラミングを続けていけば、このOn Error Resume Nextステートメントを
 使わなければならない事象は必ず出てきます。その時に正しくこれを使えるように
 しておかなければなりません。きちんとこれを押さえておかない方がとんでもない使い方で
 正しく動作しないコードを何度か見かけたことがありますので、注意してください。

 これで一応は、何となくは、作動していますし、これで完成でもまあ良いのですが・・・。
 でも、Excelは、同時に複数のセルに入力することが可能です。
 この時の対応が成されていません。エラーにならないだけです。
 又、入力データのチェックもあと少し必要です。

 これは、何度かお使いになって、細かい動作の不備を見つけてください。

 Ichinose


Ichinoseさん有難うございます。
無事になんとか使用できました。すごく助かります。

On Error Resume Nextは

>実行時エラーが発生してもプログラムを中断せず、エラーが発生したステートメントの次のステートメントから実行を継続します。

そうだったんですね。
例題を見つけた時もなんでコード間に入れているのか、しかも何か所も
これでようやく納得です。^^;;

>これは、何度かお使いになって、細かい動作の不備を見つけてください。
はい。また何かありましたら、教えてください。理解遅いですが、よろしくお願いします。m(_ _)m

寧々


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.