TADOQueryのCursorLocationについて

解決


yamada  2013-06-13 23:18:51  No: 44669

Delphi XE3でSQL SERVERへTADOConnectionで接続しています。

TADOQueryで200万件ほどのレコードをオープンします。

https://groups.google.com/forum/#!msg/delphicbuilder-ml-archive/G15ux5uMois/C_ZHzOvHxEoJ
を参考にして、
1) TADOataSet の ExecuteOptions を [eoAsynchFetch] に設定
2) TADODataSet の CacheSize を 100 程度に設定
をやってみましたが、どうもメモリ不足で落ちます。

試行錯誤しまして、TADOQuery側のCursorLocationをclUseServerにしたところ、
時間はかかりますが、オープン出来ました。

ただし、同時に別の実行ファイルで同様のテーブルを開き、
元の実行ファイルで全く同じ条件でTADOQueryでオープンしたところ、
レコードカウントが-1で返ってきてしまいます。

別の実行ファイルでのTADOQuery側のCursorLocationは
clUseClient、clUseServerどちらに設定してもダメでした。

どう対処すればよいかご教授お願いします。


au  2013-06-14 23:09:15  No: 44670

レコードカウントとは、TADODataSetのRecordCountプロパティのことですか?

ttp://support.microsoft.com/kb/194973/ja
CursorTypeをctKeyset又はctStaticにすれば取得出来るように書いてあります。

又は、SQLのCOUNT(*)を使ってレコード数を取得するようにしたらよいのではないでしょうか?


yamada  2013-06-14 23:22:57  No: 44671

au様
ありがとうございます。

CursorTypeをctKeyset、ctStaticにしてみましたが、
やはり-1で返ってきてしまいます。

件数に関してはもちろん、
SQLのCOUNT(*)にすれば取得はできますが、
件数確認のため、RecordCountプロパティを見ました。

根本的に、同様のクエリーを走らせているのにもかかわらず、
データが取得できないのです。0件ではなく、-1件です。

CursorLocationとはなんぞや、というところを、
知識不足の為、調べておるところです。

皆さん、取得する件数が多い時は
どうされてるのでしょうかね。。

よろしくお願いします。


HOta  2013-06-15 00:25:51  No: 44672

私なら、素直にSelect count(Field1)
でいきます。


yamada  2013-06-15 01:46:58  No: 44673

HOta様

ありがとうございます。
すいません、カウントのみを取得したいのではなく、
通常のデータを取得したいのです。

今作成しているプログラムは
ストリンググリッドに取得したデータを表示させるものです。

その際にSQLでデータが取得できないのです。

説明足らずで申し訳ございません。


snail  2013-06-17 20:33:13  No: 44674

>今作成しているプログラムは
>ストリンググリッドに取得したデータを表示させるものです。
200万件は表示できないですよね。何らかのKEYで必要な部分を
小分けにして取り出すしか方法はないと思います。


au  2013-06-17 20:45:57  No: 44675

先のリンク先にもちょこっと書いてあるんですが、サーバーサイドカーソルを使用する場合は、RecordCountプロパティでレコード数を取得出来ない事 も 
あるようです。というか、取得出来ない事の方が多そうな感じというか取得出来ることを期待しない方が良い感じというか。

なので、私はグリッドにデータを表示する場合であろうとレコード数を取得したい場合は、データの取得とレコード数の取得の2回クエリを実行します。


yamada  2013-06-18 02:42:59  No: 44676

snail様、au様

ありがとうございます。

自己解決しました。
TADOQueryプロパティの
CursorLocationをclUseServer
LockTypeをltReadOnlyにすると、
データを取得できるようになりました。
今回は読出しのみのプログラムなので、この設定で作成することにしました。

RecordCountプロパティでの取得できないという旨は
覚えておきます。ありがとうございます。
SQLのCOUNT(Field1)だけなら、そんなに負荷もかからないですし、
今後はそうすることに致します。

皆様ありがとうございました。


Syake  2013-06-21 02:39:03  No: 44677

解決のご様子ですが・・・。

何故なんでしょうか?
DBによって違うのでしょうか?
ADOではSQLServer,MDBを使っていますが、RecordCountが取得できない
なんて事はありませんでした。
ADOは前方向カーソルなどRecordCountが取得できない場合は-1を返します。
私はRecordCountを知りたい場合はclUseClient、ctStaticに設定します。
静的カーソルで無いとデータセットの正しいレコード数がわからないし、
取得後も静的なので不変だからです。

clUseClientにctStatic以外を設定しても意味がありません。
逆にclUseServerにctStaticを設定しても意味はありません。
クライアントにカーソルがある場合は、静的以外には無く
サーバーにカーソルがある場合は静的にできないからです。
設定してもADOは実行時にプロパティ値を変えてしまいます。

サーバーカーソルで例えRecordCountが取得できたとしても、それは
その後のデータセットの正確なレコード数では無くなる事もありえます。
これはCount(*)で事前にレコード数を取得した場合と全く同じ事と言え
ます。

なので、余計なお世話ですがCount(*)でレコード数を取得した後に
再度抽出文を発行したデータ操作にその値は使わない方が良いと思い
ます。
そんなことは絶対に無い仕様なら構いませんが、Count(*)と次点の
SELECT文の間にデータが変更される事もあるからです。
危険性があるなら参考程度になるコードを書くべきだと思います。
これは仮にclUseServerでRecorCountが取得できたとしても同様なのは
同じ事が言えるからです。

と持論を展開してみました。


yamada  2013-06-21 23:08:53  No: 44678

Syake様

ご説明ありがとうございます。

現状を報告しますと、
CursorLocationをclUseServer
CursorTypeをctKeyset
LockTypeをltReadOnly
になっております。

RecordCountが取得できなかった件をもう少し説明しますと、
Select * from table1という簡単なクエリですが、
私の計測していたところ、10万件ぐらいまででしたら、
問題なく取得できていたのですが、
50万件を超えた辺りから-1で帰ってくるようになりました。
上記設定にプラスして、もしかすると
ExecuteOptions を [eoAsynchFetch] に設定など非同期にしていたかもしれません。

レコードカウントを取得していたのは、
単にfunction上でresult:=recordcount;にしていた為です。

>サーバーカーソルで例えRecordCountが取得できたとしても、それは
その後のデータセットの正確なレコード数では無くなる事もありえます。

はごもっともで、とりあえず、今回は読出しのみで、
複数アクセスもなく、読出している間はレコードを触れない設計になっているので、とりあえず解決とさせて頂きました。

>危険性があるなら参考程度になるコードを書くべきだと思います。
はどういったことでしょうか?

逆に私の方から、Syakeへ、大量のレコードの読出しは
SQL SERVERの設定、Delphiからのアクセス方法も含め、
どのように行われているのか、すごくお聞きしたいです。

今回は最大で200万件程度の想定でした。


Syake  2013-06-26 19:38:13  No: 44679

大量のデータ取得に関して
200万件程度・・・は想定にありません。

当方では有っても、一度に3万件程度です。
最もテストで60〜90万件程度は取得したことはありますが
RecordCountは取得できています。
但し前投稿でも記したようにclUseClient,ctStaticです。
SQLServerは特に設定を変えていません。(Defaultのままです)
現在使用しているのは、SQLServer7.0,2000,2005,2008です。
TADODataSetもCursorLocation、CursorType、LockTypeを変えるくらいです。
もっとも、clUseClient,ctStaticは実コードでは保々使用していません。
レコード件数と言っても、テーブル自体の中身が違うと思われますので、
何とも言えませんが。

一般的に動的カーソルでは結果セットが定まらないので-1を返します。
ctKeySetに関して私の解釈に誤りが無ければ静的カーソルと動的カーソル
の中間です。
なので・・・。

>逆に私の方から、Syakeへ、大量のレコードの読出しは・・・

取り敢ずレコード数が欲しい場合はCount()を使用しています。
大量のレコードの読出し(参照)ならclUseServer,ctOpenForwardOnlyです。
但し、これでも200万件となると時間がかかりますね。
なのでTDBXReaderを使用するかもしれません。

>危険性があるなら参考程度になるコードを書くべきだと思います。
>はどういったことでしょうか?

Select count(*) 文からSelect * 文の間で、レコードが変更さる恐れが
ある場合を仮定しての話です。
もちろんこの場合、Select * を動的カーソルで参照する場合も当てはま
ります。
レコード数が定まっていると仮定したコードを記述するべきでないと言う
ことでした。
例えばレコードを順次参照するのに進捗バーのMaxにレコード数を設定し
そのままFor文するとか・・・
こうしたい場合は
    PotisionにMax以上の数値が設定されない様にする。
    レコードの末尾に達したか必ずチェックする。
などの小細工が必要などと老婆心です(^^;)。

最も、貴兄の場合ではレコードが変更される事は無いとのことなので、
この心配は不要でしょう。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加