Laboratory of Mobile Agricultural Chemicals Searcher
携帯農薬検索実験室

研究会

  ツリー表示 ┃スレッド表示 ┃一覧表示 ┃トピック表示 ┃番号順表示 ┃検索  
80 / 114 ツリー <前へ | 次へ>

〔327〕ACFinder 060620版 kabe (06/06/20 23:21)

〔350〕Re:テーブルの構成 Hidemi Oya (06/06/25 3:04)
〔352〕Re:テーブルの構成 kabe (06/06/25 15:03)
〔354〕Excel 読み込み高速化 Hidemi Oya (06/06/25 16:35)

〔350〕Re:テーブルの構成
 Hidemi Oya WEB  (06/06/25 3:04)

引用なし
   kabe さん、こん**は。Hidemi Oya です。

>いや、insert into するフィールド数は逆に少なくなるので、これも少しは貢献しているかもしれません。
 そうか、今までも Sheets[No].Strings[Row] で取り出したカンマテキストをそのまま INSERT 文の値として渡すんじゃなくて、ダブルクォートしたりするルーチンが入っていたので、フィールドを抜く処理を追加してもそれほど大きな影響はないんですね。それよりも、DROP TABLE と同様に INSERT するフィールド数が少ない方が書き込みが速くなる影響の方が大きいと…。

 ところで、現在は Excel ファイルを見えない TStringGrid か何かに一括して保存してから SQLite に書き込んでますよね? TStringGrid や TStringList は、データ数が多くなるとだんだん遅くなるので、下記のように1行読むごとに INSERT 文を発行する方式にすれば、もっと速くなりませんかね?

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, ComCtrls, StdCtrls, ExtCtrls, XBiff;

type
 TForm1 = class(TForm)
  XBiff1: TXBiff;
  Button1: TButton;
  ProgressBar1: TProgressBar;
  procedure Button1Click(Sender: TObject);
  procedure XBiff1Progress(Sender: TObject; Progress: Integer);
  procedure XBiff1ReadCell(Sender: TObject; SheetNo, Row, Col,
   rgbAttr: Integer; Data: String; var Cancel: Boolean);
  procedure XBiff1ReadSheetName(Sender: TObject; SheetName: String);
 private { Private 宣言 }
  FValidSheet: boolean;
  FRow: TStringList;
  procedure InsertRow;
 public { Public 宣言 }
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

function HanToZen(s: string): string;
begin
 // 実際には変換ルーチンが入る
 Result := s;
end;

procedure TForm1.InsertRow;
var
 sql: string;
begin
 if FRow.Text = '' then Exit;
 sql := 'INSERT INTO tekiyo VALUES(' + FRow.DelimitedText + ')';
 FRow.Clear;
 // ここで SQLite に INSERT 文発行
end;

// Excel ファイル読み込み&SQLiteに保存
procedure TForm1.Button1Click(Sender: TObject);
begin
 FRow := TStringList.Create;
 try
  FRow.QuoteChar := '''';
  ProgressBar1.Position := 0;
  XBiff1.LoadFile('test.xls');
  InsertRow; // 最後の行を保存
 finally
  FRow.Free;
 end;
end;

procedure TForm1.XBiff1Progress(Sender: TObject; Progress: Integer);
begin
 ProgressBar1.Position := Progress;
end;

procedure TForm1.XBiff1ReadSheetName(Sender: TObject; SheetName: String);
begin
 FValidSheet := SheetName = '登録適用部';
end;

procedure TForm1.XBiff1ReadCell(Sender: TObject; SheetNo, Row, Col,
 rgbAttr: Integer; Data: String; var Cancel: Boolean);
begin
 if not FValidSheet then Exit;
 if Row = 0 then Exit;
 case Col of
  5..8, 13, 18: FRow.Strings[Col - 4] := AnsiToUtf8(AnsiQuotedStr(HanToZen(Data), '"'));
  9..12, 14..17: FRow.Strings[Col - 4] := AnsiToUtf8(AnsiQuotedStr(Data, '"'));
  20..24: FRow.Strings[Col - 5] := AnsiToUtf8(AnsiQuotedStr(Data, '"'));
  0: begin
   InsertRow; // 前の行を保存
   FRow.DelimitedText := Data + ',"","","","","","","","","","","","","","","","","","",""';
  end;
 end;
end;

end.

〔352〕Re:テーブルの構成
 kabe WEB  (06/06/25 15:03)

引用なし
   >Hidemi Oyaさん
kabe です。

> ところで、現在は Excel ファイルを見えない TStringGrid か何かに一括して保存してから SQLite に書き込んでますよね?
Excel入出コンポのサンプルどおり、TStringListに全部読み込んだ後に、SQLite に書き込んでいます。
正確にはSQLite に書き込む前に、列数をそろえるため(使用回数1〜5が行ごとに異なるため)もう1回StringListのCount分ループを回しています。

>TStringGrid や TStringList は、データ数が多くなるとだんだん遅くなるので、下記のように1行読むごとに INSERT 文を発行する方式にすれば、もっと速くなりませんかね?
ちょっと、試してみましたが、どうもうまくいきません。
ReadCellイベントでうまくデータが拾えず挫折しています。(;_;
もちろん、[#350]のままのコードではないので、どこか不具合あるのだと思いますが、うまく動かすにはちょっと時間がかかりそうです。

ただ Excel の読み込み処理に一番時間がかかっているので、もう少し改善できないか取り組んでみます。
>FRow.DelimitedText := Data + ',"","","","","","","","","","","","","","","","","","",""';
0列目で最初に列数分、初期化すればいいんですね。
このあたり、参考になりそうです。

〔354〕Excel 読み込み高速化
 Hidemi Oya WEB  (06/06/25 16:35)

引用なし
   kabe さん、こん**は。Hidemi Oya です。

>Excel入出コンポのサンプルどおり、TStringListに全部読み込んだ後に、SQLite に書き込んでいます。
 TStringList は、件数が増えると Add が目に見えて遅くなります。データの行データを全て読み込むのはちょっときついです。1行分の列データを保存する程度なら、それほど遅くありません。
 ってことで、1行分のデータを読み込むたびに INSERT 文を発行した方が高速化できる可能性が高いです。

>ReadCellイベントでうまくデータが拾えず挫折しています。(;_;
 私も細かくは検証していませんが、それなりに問題なさそうに思えましたが…。具体的に、どのようなデータが読めないのでしょう?
 サンプルだと、Col = 0 の時に行が変わったと判断して INSERT 文を発行していますが、Col = 0 にデータがない場合は、これだとダメですね。適用データは必ず登録番号があるのでこれでも大丈夫だと思いますが、Row 番号をメモリに保存しておいて、その値と現在の Row 番号が異なったら INSERT 文を発行する方が確実です。

>>FRow.DelimitedText := Data + ',"","","","","","","","","","","","","","","","","","",""';
>0列目で最初に列数分、初期化すればいいんですね。
 最初は OnReadCell イベントが発生するたびに単純に Add してたんですが、これだと列番号とデータが合いませんでした。それで、セルにデータがない時は OnReadCell イベントが発生しないことに気が付いた次第です(^_^;)。で、先にデータ領域を用意しておく方法として、これが最も単純な方法かなあと…。
 string の配列を用意しておいても良いのですが、あとでクォート付きカンマテキストに変換することを考えると、TStringList を使った方が楽ですから(速度的には不利になるかもしれませんが)。QuoteChar をシングルクォートにしておいて、CommaText ではなく Delimited テキストを使用することにより、空白データもそのまま初期化できますし…。なお、整数データの部分は、ダブルクォートなしにすれば OK です。

  ツリー表示 ┃スレッド表示 ┃一覧表示 ┃トピック表示 ┃番号順表示 ┃検索  
80 / 114 ツリー <前へ | 次へ>
ページ:  ┃  記事番号:   
(SS)C-BOARD vv3.8 is Free.