MIT’s blog

個人的なメモかな

CsvHelperで楽させて頂く

もう何年前に書いたのか忘れてるツールの機能を追加する為に

A .NET library for reading and writing CSV files. Extremely fast, flexible, and easy to use. | CsvHelperを使ってみる。

プロジェクトへの追加はパッケージマネージャコンソールで

PM> Install-Package CsvHelper

とする。まあ、Visual Studioのメニューからでも出来ますね。

 

で、使うソースに

using CsvHelper;
using CsvHelper.Configuration;

追加して、今回の入力CSVファイルであるダビジェネのテキスト出力はレースヘッダとそのレースの出走馬毎のデータからなる2種類のCSVが混在するのでそれぞれのクラスとそのマップクラス。更に出力するターゲット用外部指数ファイルのクラスとそのマップクラスを定義。

csv.Context.RegisterClassMap<DGRaceTable>();
csv.Context.RegisterClassMap<DGUmaTable>();

って感じで2種類のマップを指定して

csv.Read();

した後にそれぞれ該当する行を

DGRaceRecords.Add(csv.GetRecord<DGRace>());

または

DGUmaRecords.Add(csv.GetRecord<DGUma>());

としてListに追加して、そのListを使ってターゲット用のListを作って書込みです。その昔はベタに読込だファイルから自力でパースしながらコンバートした気がしますが、今回一度CsvHelper使ってある程度使い方を理解したので、CSVファルイ関連は今後楽出来るかなぁと。

二重起動禁止

少しご無沙汰でした。5月22日になったばかりの午前1時過ぎに体の異変で目が覚めた。風邪の症状で、念の為に体温計ると37.5℃で基本熱が出ない体質でコロナワクチン2回目の時に37.1℃まで上がったのが何十年ぶりだったんですが、ああ、こりゃコロナだと思い24時間対応って発熱相談に電話するも普通に週明けまで待って掛かりつけにて受診して下さいってな回答。日曜日は部屋に篭り、市販の風邪薬飲んで氷枕してたら少し落ち着いた。月曜日に掛かりつけの病院にコロナっぽいと電話してから受診し陽性。病院から保健所に通報されて自宅で保健所からの連絡待ち。自宅療養だと家族が危険なのでホテル療養希望して火曜日(5/24)から10日間程退屈な生活を。そんな訳で約2週間パソコンに触れない生活でした。

 

本題ですが、毎度の事でグーグル先生に問い合わせた所、何度もお世話になっているdobon.netさんの二重起動を禁止する - .NET Tips (VB.NET,C#...)を参考に実装を試みるが「すべてのユーザーに二重起動を禁止する」で書かれている「MutexSecurityを指定できるMutexコンストラクタ」は何処から使えるのか僕には分からずに断念。少なくともうちの現在の環境(Windows11 Pro, Visual Studio Community 2022 Ver.17.2.3)ではソース記述時点でエラーです。4つも引数があるコンストラクタは無いよってorz
少し調べると他サイトの解説記事にも4つ引数の例があるので、単純にusingが足りてないだけだとは思う。


で、@IT.NET TIPS Windowsアプリケーションの多重起動を禁止するには? - C# VB.NET Windowsフォーム - @IT (itmedia.co.jp)を使わせて頂きました。

キー項目

JRA-VANの提供データは仕様書なんかで諸々の定義がされてます。そんな中でキーにする項目で微妙な記述があります。馬毎レース情報のキーなんですが、馬番をキーにするか否か。

出走馬名表時点では、全て初期値を設定(出馬表時点で馬番が埋まるため、馬番を純粋に
キー設定すると、同一馬の情報が重複する)
ただし、レースによっては枠番・馬番を設定している場合がある

海外レースにおいては、馬番が不明の場合は初期値を馬番として入力
なお、過去のレースでは連番を設定しているものがあるため、血統登録番号をキーとして
使用する


こんなんで、悩んでました。馬番をキーから外して昨夜フルセットアップして寝て、今朝から最適化。しかし、キーから外すと海外レース情報なんかで問題が有り、元に戻す。こんな繰り返しで再度フルセットアップしてますが、以前の様に14時間とか掛かってたのが2時間程度なのは本当に助かる。

で、多分問題になるのは出馬表確定前の提供データでは馬番が無い状態で、その後に馬番確定して提供されるのでキーとして馬番違う為、同一馬に対して2つのレコードが存在してしまう。これをどの段階でチェックして削除するか考えてみます。

BackgroundWorkerコンポーネント

DoEventsではなく、マルチスレッド化で対処するのが今風?らしいが、これはなかなかハードルが高い。そもそも、JV-Link自体が32bitだったり諸々の事情からなのかマルチスレッドでの動作が遅くなる模様なので今回は宿題として残します。ってのもデータベース構築であまりにも時間掛かり過ぎて一向に開発進まないので、他の作り込み進めます。

まあ何にしても14時間掛かっていたフルセットアップが、今回高速モードオプション装備して、登録してるレコードの情報やログのリアルタイム更新を捨てて処理すれば2時間程度で可能になったので、フルセットアップも気軽にやり直せるのは嬉しい。高速モードじゃなくても6時間だったかな^^; 別にフルセットアップを延々と何登録してるか見てるってのもね~w これはここまでの名残として残すのもありかなって程度です。

結局の所、現在のデータベースは51ギガバイトと相変わらずなかなかのサイズです。最適化には2時間以上ですが、これも妥協ですね。

Application.DoEventsは古い?

Windowsでのプログラミングの基本をVisual Basicで始めたのはもう30年近く前になるのかな~^^; ループで回して長い処理する時にはDoEvents入れよう的な習慣が染みついてたと思うんですが、それは昔の話? まあ、当初Windows2000WindowsNTだったのかな?OSがMS-DOSでその上に乗るWindows Ver2.1辺りから使ってますが、当初はマルチタスクすら完全じゃなかったが、今ではしっかりとOSが頑張ってくれてる(?)ので、あまり意識する必要が無くなってるのかな?

4万件程度のレコード追加処理が非常に時間が掛かっており、バルクインサートまで試してみたのですが、一向に改善されなくて、ボトルネックを探ししてたんですが、SQLite関連全てコメントアウトして処理しても10%程度しか時間が減らず、ループの根本的な改善が必要なのかな~っと見てたんですが、

Application.DoEvents();

が時間食ってましたorz 4万件の登録で80秒がこれ削るだけで7秒とかに改善しました。ただ、当たり前ですが、その見返りとして、ステータスバーに登録してるレコードの簡単な表示したり、ログに出力してるんですが、これらがステータスバーの表示はされず、まあ、でもプログレスバーは動いてくれているのでハングではなく処理してるのは視覚的に分かるのはありがたいです。この辺りがOSがきっちりとマルチに動作してるおかげなんですかね? でも、ログの表示もリアルタイムじゃなく行毎にだったり、ログをスクロールバックして前を見ようとしても反応してくれないし、まあ、そもそも、アプリをクリックしても最前列に来ないとか...

代わりのってか、DoEventsで処理させていたものを個々に必要な部分のみ行う修正をして行かなきゃならなくなりました。フルセットアップなり更新処理なりの間に必須な事かは別として、可能な範囲はやってみたいと思います。

Parameterの準備

1レコードに1,700以上の項目があるテーブルの登録処理に時間が掛かってたんですが、このやり方を改善してみました。これまでは

cmd.Parameters.Add(new SQLiteParameter("@PARA", value));

って感じで毎回やってたんですが、事前に

cmd.Parameters.Add(new SQLiteParameter("@PARA", dummy));

とパラメーターは追加しておき、レコード追加時は値のみを

cmd.Parameters[0].Value = value;

として全てのパラメーターが準備出来たら

cmd.Prepare();
cmd.ExecuteNonQuery();

とする事でかなりの時短が出来ました。このテーブルは元ファイルから約2,500レコード分のデータを登録してまして80秒前後掛かってましたが、今回の修正で4秒とか劇的に早くなりました^^

 

で、全ての修正はまだなんですが、昨夜フルセットアップしてみた所、これまで13時間だったものが半分以下の6時間弱に改善しててほっとしてます。ただ、まだまだ項目数が少ないけど元ファイルから数万レコード分登録するテーブルがあるんですが、これが4万レコードとかだと30秒以上掛かっているのを何とかしなきゃです。接続文字列は今回はデフォルトに戻してフルセットアップしました。これはdatabase is busyのエラーがトラップ出来ずに延々とハマる時があるので、それを回避する為です。

今回の修正を全てに行った後にまたフルセットアップがどの程度まで短縮されるかは分かりませんが、今回程は期待出来ないので何か考えないとダメかも^^;