質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.86%

  • VBA

    1551questions

    VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

TextBox Changeイベントを利用した検索フォームの速度について

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 505

zorac

score 32

前提・実現したいこと

TextBoxとListViewを組み合わせて、1文字入力される度に検索結果を更新する検索フォームを作りたいと思っています。
Googleインスタント検索と同じような挙動を期待して、ひとまず動くものを作りました。
ソースを動かすためには下記要件を満たして頂く必要があります。

  1. Sheet1のA列からG列の2行目以降に検索対象となるデータを入力
  2. TextBoxをオブジェクト名TextBox1として準備
  3. ListViewをオブジェクト名ListView1として準備

発生している問題・エラーメッセージ

TextBoxに文字を入力していくと期待通りの動作をし、速度も実用上問題のないレベルです。
しかし、BackSpaceを押してTextBoxに入力済みの検索ワードを削除していくと、途端に検索に掛かる時間が長くなります。
なぜ検索ワードを消した際の検索が遅くなってしまうのか、どうすれば速く出来るのかアドバイスをお願いします。
同じような事をもっとスマートに実現する方法があればそれについても教えて頂きたく思います。
宜しくお願いします。

速度計測結果

実際に15,000件弱のデータを対象に検索を実行し、実行時間を計測しました。(単位:秒)
TextBoxに文字を入力していく場合(検索ワードを4文字入力した場合の計測結果)
データの検索:       0.1
ListViewへの反映:  0.32

データの検索:       0.09
ListViewへの反映:  0.16

データの検索:       0.08
ListViewへの反映:  0.04

データの検索:       0.08
ListViewへの反映:  0

TextBoxの文字を1文字ずつ削除していく場合(入力済みの4文字を削除していく場合の計測結果)
データの検索:       1.52
ListViewへの反映:  0

データの検索:       1.52
ListViewへの反映:  0.05

データの検索:       1.55
ListViewへの反映:  0.16

データの検索:       1.61
ListViewへの反映:  0.33

該当のソースコード

'郵便番号データダウンロードサービスで入手可能な郵便番号データを利用する
Private Sub UserForm_Initialize()
    With ListView1
        .View = lvwReport
        .HideSelection = False
        .FullRowSelect = True
        .ColumnHeaders.Add , "_X0401-X0402", "全国地方公共団体コード", 120
        .ColumnHeaders.Add , "_OldPostalCode", "旧郵便番号", 120
        .ColumnHeaders.Add , "_PostalCode", "郵便番号", 80
        .ColumnHeaders.Add , "_Prefectures", "都道府県名", 80
    End With
End Sub


Private Sub TextBox1_Change()
    '-------------------- ListViewのクリア ココカラ --------------------
    ListView1.ListItems.Clear
    '-------------------- ListViewのクリア ココマデ --------------------


    '-------------------- データの検索 ココカラ --------------------
    Dim t As Single
    Dim ws As Worksheet
    t = Timer()
    Set ws = Worksheets("Sheet1")

    With ws
        Dim lastRow As Long
        Dim dataSet As Variant
        lastRow = .Cells(.Rows.count, 1).End(xlUp).Row
        dataSet = .Range(.Cells(1, 1), .Cells(lastRow, 4))

        Dim i As Long
        For i = 2 To lastRow
            If dataSet(i, 3) Like "*" & TextBox1 & "*" Then
                With ListView1.ListItems.Add
                    .Text = dataSet(i, 1)               '全国地方公共団体コード
                    .SubItems(1) = dataSet(i, 2)        '旧郵便番号
                    .SubItems(2) = dataSet(i, 3)        '郵便番号
                    .SubItems(3) = dataSet(i, 4)        '都道府県名
                End With
            End If
        Next i
    End With
    Debug.Print "データの検索: " & Round(Timer() - t, 2)
    '-------------------- データの検索 ココマデ --------------------
End Sub

補足情報(言語/FW/ツール等のバージョンなど)

Excel 2016 (office 365)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • imihito

    2018/01/04 19:26

    データ検索時の`If dataSet(i, 9) Like "*" & TextBox1 & "*" Then`ですが間違っていないでしょうか?

    キャンセル

  • zorac

    2018/01/04 19:40

    ご指摘ありがとうございます。質問するに当たって簡略化した際、変更が漏れました。質問文も修正しておきました。

    キャンセル

回答 3

+1

勘違いしていたらすみませんが削除をした方が遅いというのは当たり前ではないですか?
提示されている部分とテスト方法をみると「4文字入力して、4文字消す」という方法に見えます。
最初はchengeイベントが発生しないため、0文字目の入力が存在していません。
"1234"を入力すると仮定すると

入力時は
"1"の部分一致
"12"の部分一致
"123"の部分一致
"1234"の部分一致

削除時は
"123"の部分一致
"12"の部分一致
"1"の部分一致
""の部分一致

になります。当然削除時のほうが一致する数量が多く最後には全件一致が発生しているので
時間がかかるのでは?

  Debug.Print "データの検索: " & Round(Timer() - t, 2),TextBox1

として本当に同じ検索(上の例では"1234"からの"4"削除時と"12"からの"3"入力時)で
タイムが違うのか提示された方がいいです。自分の理解力不足でしたらすみません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/06 15:10 編集

    回答ありがとうございます。
    ご指摘の通り、私の質問の仕方では問題点が分かり難いですね。
    検索ワード毎の計測結果が分かりやすいようまとめました。
    []内は検索ワードです。

    入力していく場合の計測結果
    検索ワード[9]: 0.42s
    検索ワード[90]: 0.25s
    検索ワード[903]: 0.12s

    削除していく場合の計測結果
    検索ワード[903]: 1.52s
    検索ワード[90]: 1.57s
    検索ワード[9]: 1.71s

    このように何故か削除していく場合だけ、同じ検索のはずなのに動作が異常に遅く、
    原因が分からずに悩んでいます。
    今回はChangeイベントを使用せず、検索ボタン設置の方向で解決したいと思います。
    ありがとうございました。

    キャンセル

checkベストアンサー

0

Changeイベントは1文字変更されても発生するので、AfterUpdateか明示的に検索用のコマンドボタンで行う方が良いかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/06 15:03

    回答ありがとうございます。
    AfterUpdateを使う場合は入力後にフォーカスの移動が必要になるので、検索ボタンを用意する方が実用的かもしれませんね。
    リアルタイムに検索出来るようなものがあったら直感的で良いなと思って取り組んでみましたが、
    解決が難しそうなので、検索ボタンを付ける方向で検討を進めたいと思います。

    キャンセル

0

今回の遅くなっている原因は不明ですが、
早くなるかもしれない点と、コードの書き方について幾つか。

・処理の前に、画面更新の停止、数式の自動計算を手動計算にし、
処理の後に元に戻す(高速化の定石)

・Like を InStr にする(単なる文字の検索だけなら、Likeより早くなるはず)

・searchResultを使わず、見つかったらすぐにListViewへデータ追加する。
※データが数万件なのでメモリ不足等はないかもだけど、
配列に格納しておく理由が見つからない。
もしデバッグ用なら、1行分の配列があれば十分かな。
(どちらにしても、データ検索開始前にListViewをクリアしておくべきでしょう)

・「Worksheets("Sheet1")」はWorkSheet型変数に格納して使う
※インテリセンスが使えて便利

・.Cells(1048576, 1) の「1048576」は「.Rows.Count」が妥当
※Excelのバージョンアップで行数が増えた場合に正しくなくなる

・dataSet(i, 1)の「1」等はEnumで定義すると、
dataSet(i, E_Col.ID)等と書けるので可読性があがる

以上、参考まで。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/05 23:24

    回答ありがとうございます。いまから1つ1つ確認させて頂きます。

    キャンセル

  • 2018/01/06 00:25

    ・処理の前に、画面更新の停止、数式の自動計算を手動計算にし、処理の後に元に戻す(高速化の定石)
    ⇒この設定を入れると誤差の範囲かもしれませんが、0.01sほど遅くなるようです。速くはなりませんでした。
    シート操作を行う際はこの設定が有効であることを理解していますが、
    今回のようにフォームコントロールのみの操作でもこの設定は効果があるのでしょうか。


    ・LikeをInstrにする
    ⇒実際に変更して計測してみたところ、Likeの方が早いようです。
    【計測結果】 4文字入力して比較
    Like
    0.54s , 0.26s , 0.05s , 0.04s
    Instr
    0.60s , 0.32s , 0.12s , 0.09s


    ・searchResultを使わず、見つかったらすぐにListViewへデータ追加する。
    ※データが数万件なのでメモリ不足等はないかもだけど、配列に格納しておく理由が見つからない。
    ⇒遅いのは検索であってListViewの操作ではなさそうだということを明示する為、質問に当たって処理を分けました。
    実際のコードではsearchResultを経由せずに実装することを考えています。
    今回良い機会だったので速度を調べてみましたが、searchResultを使った方が若干速くなるようです。
    【計測結果】 4文字入力して比較
    searchResultを使う
    0.50s , 0.25s , 0.05s , 0.04s
    searchResultを使わない
    0.54s , 0.26s , 0.05s , 0.04s


    ・「Worksheets("Sheet1")」はWorkSheet型変数に格納して使う
    ※インテリセンスが使えて便利
    ⇒サンプルコードなどでこの用法をよく見かけますが、メリットが分からず使っていませんでした。
    確かにインテリセンスが使えるのは便利ですね。今後活用させて頂きます。


    ・.Cells(1048576, 1) の「1048576」は「.Rows.Count」が妥当
    ※Excelのバージョンアップで行数が増えた場合に正しくなくなる
    ⇒いつも1048576を定数化して使っていましたが、確かに先々の事を考えるとrows.countでやっておいた方が良さそうですね。
    今後活用させて頂きます。

    ・dataSet(i, 1)の「1」等はEnumで定義すると、
    dataSet(i, E_Col.ID)等と書けるので可読性があがる
    ⇒今回データまで提示できなかった為、こういった配慮は出来ませんでしたが、
    次回以降、可読性についてもう少し考えさせて頂きます。


    色々とアドバイスを頂きましてありがとうございました。
    質問文のソースも後ほど差し替えておきます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    VBA A,B,C…列方向へ検索値を移動したい

    お世話になっております。 下記、コードについて教えてください。 Sub 水平変位データ() Dim kensakuA As Range '検索する値 Dim

  • 受付中

    ExcelVBAについて急ぎです。どなたか教えてください。

    前提・実現したいこと ExcelVBAについてです。ある列に、昇順に文字列が並んでいます。昇順ではありますが、条件分岐させる際、同じとみなされる文字列(01××、01○○、01△

  • 受付中

    VBAを用いてバッチファイルを作成するツール

    前提・実現したいこと 業務でVBAを用いてバッチファイルを作成するツールが必要となりました。 ActiveDirectoryのUID棚卸しの為に使用します。 添付画像のような

  • 解決済

    VBAの配列の質問

    VBAの配列で教えてほしい事があります。 同一ブック内に2つのsheetがあります。 ”集計結果”と”一時保管”です。 ”一時保管”はA~D列にデータが入っています。

  • 解決済

    VBAで90列ごとに改ページをする方法について

    VBAの改ページについて質問です。 アクティブシートに90列ごとに改ページを挿入したいです。 現在このコードで400エラーが出てしまい、 74列目で改ページが挟まれてしまうので

  • 解決済

    VBA高速化について

    20個のエクセルファイルを読み込み、特定のシートにあるテーブルから特定の値を探し出し、その右横にあるセルの値を取り出します。 集計用のエクセルのテーブルでも、同じ特定の値をテーブル

  • 解決済

    For ~ Next 繰返し処理の最後で意図しない値を得てしまいます。

    Dim p As Integer Dim yearF As Integer p = 3 Worksheets("list").Select For

  • 解決済

    textファイルを文字列でエクセルに貼り付けたい

    エクセルのマクロで下記のようなコードを書いて動かしているのですが、文字列でシート1に貼り付けされません。 読み込むtextファイルは、タブ形式で区切られており、その区切りごとに各列

同じタグがついた質問を見る

  • VBA

    1551questions

    VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。