DOBON.NET DOBON.NETプログラミング掲示板過去ログ

ファイル出力時に、キー重複を避ける方法

環境/言語:[VB.net 2003, WindowsXP]
分類:[.NET]

テキスト型ファイル(固定長)を読み込み、編集して、CSV形式ファイルで出力したいです。

単純に1行単位でテキストファイルを読み込み、CSVに吐く形で作っていましたが、
読込ファイルの中に、キー項目で重複するレコードが含まれていて、
その場合、後のデータで上書きしたいという機能が追加になりました。
DBやMDBなどを使わずに、実現することは可能でしょうか?

1行ずつループで回して重複レコードを検索するというような方法は、
レスポンスが遅くなりそうなので、できれば避けたいです。
データをメモリ上に格納して、重複レコードを判断できる方法を探しているのですが、
なかなか見つかりません。

ご存知の方がみえましたら、教えてください。
一番単純なのは、
っ[ キー列でソートして、同じ値を持つ行を削除(無視?) ]
でしょうか?
ガッさん、すばやいお返事ありがとうございます!

> [ キー列でソートして、同じ値を持つ行を削除(無視?) ]
ソートを使う方法は、あまり考えていませんでした。
というのは、ソートすると、
「後のレコードを残す」ことが難しいのではないかと考えたからです。
でも、調べても試してもいなかったので、
その方法で一度探ってみます。ありがとうございました。
■No14858に返信(pandaさんの記事)
> というのは、ソートすると、
> 「後のレコードを残す」ことが難しいのではないかと考えたからです。
インデックスを付けておいて大きい方が後とか…

ワタシの場合、1行単位でテキストファイルを読み込み、
コレクションに読み込んだ行のキーとデータを格納して
いきますねぇ。既に格納されていれば上書き…
るしぇさん、お返事ありがとうございます!

> ワタシの場合、1行単位でテキストファイルを読み込み、
> コレクションに読み込んだ行のキーとデータを格納して
> いきますねぇ。既に格納されていれば上書き…

なるほど、そういうやり方もあるんですね。
1行単位の読込がほとんど出来ているので、
これなら最小限の修正で済むかもしれません。
試してみます、ありがとうございます。
> ワタシの場合、1行単位でテキストファイルを読み込み、
> コレクションに読み込んだ行のキーとデータを格納して
> いきますねぇ。既に格納されていれば上書き…

これが自然と思いますが、データ量だけが一番の問題ですね。

代案としては、キーごとにデータファイルを作成して重複したら再作成をする、
そして最後に結合する。
#結合時のファイルの列挙という処理が、期せずしてソート処理をしてくれますね。
まどかさん、お返事ありがとうございます!

> これが自然と思いますが、データ量だけが一番の問題ですね。

私も、レスポンスが遅くなってしまうことが、心配です。
データは、2,3千、多く見ても5千件ほどです。

> 代案としては、キーごとにデータファイルを作成して重複したら再作成をする、
> そして最後に結合する。
> #結合時のファイルの列挙という処理が、期せずしてソート処理をしてくれますね。

この作りにすると、最大5千個のファイルが出来るということですか?
レスポンスがどれくらいになるのかまったく想像できませんが・・・。
結合で、列挙を使えばソートができるんですね、知りませんでした。
参考にさせていただきます。
2006/03/16(Thu) 17:50:00 編集(投稿者)

同じキーの場合は何を基準にして並べ替えるのでしょうか?ここでは仮に日付とします。
ソートは、キー > 日付 で行います。
ループしながら一つ前のレコードをしまっておき、その一つ前のレコードのキーと今読んだレコードのキーを比較し、違っていたら(ブレークしたら)、一つ前のレコードを書き出します。
ループを抜けたら、忘れずに、しまっておいた一つ前のレコードを書き出して下さい。

#ガッさんの言う通りなんですがね。(^^;
trapemiyaさん、お返事ありがとうございます!

> 同じキーの場合は何を基準にして並べ替えるのでしょうか?
入力順です。上から1件ずつ読んでいったら、下に書かれたのレコードが優先されます。

なので、教えていただいた方法だと、最初にソートして、
キーで比較し、同じなら前に退避したレコードを生かすということですよね?
ソートした時点で、ファイルに書かれてある順番が入れ替わらないか、
そこが不安なところです。
そうすると、るしぇさんのインデックスを付けてという方法が自然のような気もします。
ソートして比較できれば、レスポンスもそれほど悪くならないと思うんですけどね。
> 入力順です。上から1件ずつ読んでいったら、下に書かれたのレコードが優先されます。

であれば、データテーブルにシーケンス番号を振りながら読み込み、データテーブル上で、キー > シーケンス番号 でソートをかけます。
あとは、データテーブルを一件ずつ読みながら、上で述べた処理をすればいいんじゃないでしょうか?
後のデータが残るのなら,データを後ろから処理すればよいのでは?
コレクションに読み込んで,リバースして,先頭からチェックし,
既出のキーだったら削除,最後にまたリバースしてファイルに書き出す,
というのはどうですか。
■No14865に返信(trapemiyaさんの記事)
> あとは、データテーブルを一件ずつ読みながら、上で述べた処理をすればいいんじゃないでしょうか?

データテーブルだと一つ前のレコードがインデックス操作(今のインデックス - 1)でわかるので、一つ前のレコードを記憶しておく必要はないですね。
■No14866に返信(YASさんの記事)
> 既出のキーだったら削除,最後にまたリバースしてファイルに書き出す,
> というのはどうですか。

「既出のキーだったら」をどう判断するかがポイントですね。でも、この案も高速かもしれません。

いろんな案が出たので、純粋な興味として、後学のためにどれが一番高速なのかを検証していただくとうれしいなぁ。(^^;
trapemiyaさん、YASさん、お返事ありがとうございます!

なるほど、方法は色々ありますね。
・ファイル入力順が判断できるキーを加えて、ソートをかける
・ソートはかけずにリバースして読込済みデータのキーと見比べて、削除
・ソートはかけずに読込済みデータのキーと見比べて、上書き
・キーごとのファイルを作成し、結合する
まとめると、この4つの方法でしょうか。
可能不可能、レスポンスにかかる時間を含め、試してみたいと思います。

お返事いただいた、たくさんの方々、本当にありがとうございました。
4方法を簡単に作り、処理時間を検証しました。
テストデータとして、
取込件数 140件
重複 40件
出力件数 100件
を用意しました。

結果、一番早かったのが、
・ファイル入力順が判断できるキーを加えて、ソートをかける(1秒)

・キーごとのファイルを作成し、結合する(1、2秒ほど)
・ソートはかけずにリバースして読込済みデータのキーと見比べて、削除(2秒)
・ソートはかけずに読込済みデータのキーと見比べて、上書き(3、4秒)
となりました。
もちろん、テストデータも実際よりは少ないので、実際のデータを使ったときにどうなるかわかりません。
PGの書き方によってはもっと早くなるものもあると思います。
検証も、それぞれ2,3回ほどしか実行していないので、確実なものだとは言えません。
わかったことは、ループを含むと遅くなること(予想どおり)と、キー毎のファイル出力後に結合する方法が、予想以上に早く済んだことです。
でも、今回は、千件単位のレコードを取り込むため、
入力順のキーを付け、ソートする方法でいこうと思います。

たくさんの方にご協力いただき、感謝しています。
ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板