TQuery如何承接大量的資料 |
答題得分者是:sryang
|
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
各位先進:
請教一個問題,我想要從一台 ORACLE 上的某幾個TABLE資料 匯出到另一台ORACLE. 我一開始用兩個TAdoConnection,兩個TAdoQuery,來複製資料到另一台DB,跑了三萬筆資料就掛點了。 後來我改用TDataBase 及 TQuery,結果筆數有顯著的成長,跑了十幾萬筆才掛點。 問題是這幾個TABLE都有好幾佰萬筆。 請問TQuery 在 move next record 時,有辦法釋放掉之前record的記憶體嗎。如果它一直不釋放掉不用的record,那愈後面所佔用的資源一定愈來愈多,多到系統撐不住就爆掉。 還是我該用什麼不一樣的概念及架構,才能寫出一個程式,來完成這項不可能的任務。就像DELPHI的DataPump,我也有試過DELPHI 提供的 DATAPUMP 去匯資料,它好像也有問題,在沒有錯誤訊息的狀況下,還沒匯完資料就結束了。 也許有人會懷疑,為什麼不用 EXP IMP,因為我沒有權限可以EXP。 我也有試過TOAD去DUMP資料,TOAD也被我搞到掛點。 煩請各路高手協助一下,謝謝 |
syntax
尊榮會員 發表:26 回覆:1139 積分:1258 註冊:2002-04-23 發送簡訊給我 |
有沒有考慮
1. 資料庫本身的複製功能? 2. 資料庫本身所提供的 Dump 與匯入的功能? 3. SQL 語法上的標準 dump 方式? 因為你只說你用 TAdo ,但是沒說明你到底下了何種 SQL 語法?以及你複製的方式(實際運作,不是一句話:「我將A複製到B」,這樣而已)? ===================引 用 carstyc 文 章=================== 各位先進: 請教一個問題,我想要從一台 ORACLE 上的某幾個TABLE資料 匯出到另一台ORACLE. 我一開始用兩個TAdoConnection,兩個TAdoQuery,來複製資料到另一台DB,跑了三萬筆資料就掛點了。 後來我改用TDataBase 及 TQuery,結果筆數有顯著的成長,跑了十幾萬筆才掛點。 問題是這幾個TABLE都有好幾佰萬筆。 請問TQuery 在 move next record 時,有辦法釋放掉之前record的記憶體嗎。如果它一直不釋放掉不用的record,那愈後面所佔用的資源一定愈來愈多,多到系統撐不住就爆掉。 還是我該用什麼不一樣的概念及架構,才能寫出一個程式,來完成這項不可能的任務。就像DELPHI的DataPump,我也有試過DELPHI 提供的 DATAPUMP 去匯資料,它好像也有問題,在沒有錯誤訊息的狀況下,還沒匯完資料就結束了。 也許有人會懷疑,為什麼不用 EXP IMP,因為我沒有權限可以EXP。 我也有試過TOAD去DUMP資料,TOAD也被我搞到掛點。 煩請各路高手協助一下,謝謝 |
sryang
尊榮會員 發表:39 回覆:762 積分:920 註冊:2002-06-27 發送簡訊給我 |
既然您想要用程式的方法來解決,那麼我提供一些個人經驗給您參考
首先,用 BDE 或是 ADO 都是可以的。問題在於使用的方式 您提到,您用了兩個 TQuery 或 TADOQuery。大量資料的狀況之下這是非掛不可的 因為用來 insert 進目的資料庫用的 TQuery 或 TADOQuery 會因為大量資料的加入而導致錯誤 既然您的目的是要「insert 進目的資料庫」那麼為什麼要耗用記憶體來暫存這些資料呢? 所以,個人的使用習慣是使用 TQuery.ExecSql 或是使用 TADOCommand.Execute 來執行 INSERT 的語法 並且為了速度及記憶體需求的考量,搭配分批 commit 範例: [code delphi] procedure BatchMove(); var i: integer; begin Query1.SQL.Clear; Query1.SQL.Add('SELECT FIELD1, FIELD2, FIELD3 FROM TABLE1'); Query1.Open; Query2.SQL.Clear; Query2.SQL.Add('INSERT INTO TABLE2 (FIELD1, FIELD2, FIELD3) VALUES (:FIELD1, :FIELD2, :FIELD3)'); i := 1; Database2.StartTransaction; try while not Query1.Eof do begin Query2.ParamByName('FIELD1').Value := Query1.FieldByName('FIELD1').Value; Query2.ParamByName('FIELD2').Value := Query1.FieldByName('FIELD2').Value; Query2.ParamByName('FIELD3').Value := Query1.FieldByName('FIELD3').Value; Query2.ExecSql; // 每 1000 筆 commit 一次 Inc(i); if i >= 1000 then begin i := 1; Database2.Commit; Database2.StartTransaction; end; end; finally Query1.Close; end; end; [/code]
------
歡迎參訪 "腦殘賤貓的備忘錄" http://maolaoda.blogspot.com/
編輯記錄
sryang 重新編輯於 2007-09-22 09:25:12, 註解 無‧
|
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
Syntax兄您好: 1.小弟文中有提到,因為沒有資料庫Dump的權限,所以沒辦法使用資料庫本身的複製功能,只能用SELECT 的方式把資料弄出來。 2.至於ADOQuery中下了何種語法,就最單純的 Select * from TableName 而已 至於另一個負責塞資料的ADOQuery,也只是用 Insert 的語法而已。 以上說明,謝謝 ===================引 用 syntax 文 章=================== 有沒有考慮 1. 資料庫本身的複製功能? 2. 資料庫本身所提供的 Dump 與匯入的功能? 3. SQL 語法上的標準 dump 方式? 因為你只說你用 TAdo ,但是沒說明你到底下了何種 SQL 語法?以及你複製的方式(實際運作,不是一句話:「我將A複製到B」,這樣而已)? ===================引 用 carstyc 文 章=================== 各位先進: 請教一個問題,我想要從一台 ORACLE 上的某幾個TABLE資料 匯出到另一台ORACLE. 我一開始用兩個TAdoConnection,兩個TAdoQuery,來複製資料到另一台DB,跑了三萬筆資料就掛點了。 後來我改用TDataBase 及 TQuery,結果筆數有顯著的成長,跑了十幾萬筆才掛點。 問題是這幾個TABLE都有好幾佰萬筆。 請問TQuery 在 move next record 時,有辦法釋放掉之前record的記憶體嗎。如果它一直不釋放掉不用的record,那愈後面所佔用的資源一定愈來愈多,多到系統撐不住就爆掉。 還是我該用什麼不一樣的概念及架構,才能寫出一個程式,來完成這項不可能的任務。就像DELPHI的DataPump,我也有試過DELPHI 提供的 DATAPUMP 去匯資料,它好像也有問題,在沒有錯誤訊息的狀況下,還沒匯完資料就結束了。 也許有人會懷疑,為什麼不用 EXP IMP,因為我沒有權限可以EXP。 我也有試過TOAD去DUMP資料,TOAD也被我搞到掛點。 煩請各路高手協助一下,謝謝 |
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
sryang兄您好:
感謝您的範例,但也許小弟描述的不夠清楚。 就以您的範例來說,我碰到的問題不在於Query2塞資料,Query2 Insert Data的部份我已經用其它方式處理掉了,這部份沒問題。 我的問題在於 Query1 ,如果它Select 的Table 有一仟萬筆資料時,可能執行 Query1.next 五十萬次後,Query1就會承受不了這麼大的資料量而『爆炸』了。也就是說,雖然Query1的cursor雖然停在第五十萬筆資料,但前面的四十九萬九仟九佰九十九筆資料都『暫存』在電腦的記憶體中。如此下來,就算電腦有再多的Memory,也不夠塞一仟萬筆資料。我該怎麼解決這個問題。 所以這就是我的問題所在,不知Sryang兄有了解了小弟的困擾了嗎? 以上說明,謝謝 ===================引 用 sryang 文 章=================== 既然您想要用程式的方法來解決,那麼我提供一些個人經驗給您參考 首先,用 BDE 或是 ADO 都是可以的。問題在於使用的方式 您提到,您用了兩個 TQuery 或 TADOQuery。大量資料的狀況之下這是非掛不可的 因為用來 insert 進目的資料庫用的 TQuery 或 TADOQuery 會因為大量資料的加入而導致錯誤 既然您的目的是要「insert 進目的資料庫」那麼為什麼要耗用記憶體來暫存這些資料呢? 所以,個人的使用習慣是使用 TQuery.ExecSql 或是使用 TADOCommand.Execute 來執行 INSERT 的語法 並且為了速度及記憶體需求的考量,搭配分批 commit 範例: [code delphi] procedure BatchMove(); var i: integer; begin Query1.SQL.Clear; Query1.SQL.Add('SELECT FIELD1, FIELD2, FIELD3 FROM TABLE1'); Query1.Open; Query2.SQL.Clear; Query2.SQL.Add('INSERT INTO TABLE2 (FIELD1, FIELD2, FIELD3) VALUES (:FIELD1, :FIELD2, :FIELD3)'); i := 1; Database2.StartTransaction; try while not Query1.Eof do begin Query2.ParamByName('FIELD1').Value := Query1.FieldByName('FIELD1').Value; Query2.ParamByName('FIELD2').Value := Query1.FieldByName('FIELD2').Value; Query2.ParamByName('FIELD3').Value := Query1.FieldByName('FIELD3').Value; Query2.ExecSql; // 每 1000 筆 commit 一次 Inc(i); if i >= 1000 then begin i := 1; Database2.Commit; Database2.StartTransaction; end; end; finally Query1.Close; end; end; [/code] |
shunchia63
高階會員 發表:26 回覆:141 積分:198 註冊:2007-05-22 發送簡訊給我 |
|
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
Shunchia63兄您好:
您指的分頁,是指我在SQL Command裡面直接用條件把它區分開嗎。 因為我要處理的TABLE有幾百個,雖說超過上百萬筆的Table只十幾二十個,但針對每個去找出適當的Key值,然後做不同的分頁SQL條件,也是件蠻麻煩的事。 我只是想了解一下,一般像有些工具,可以將不同的資料庫資料轉移到另一台資料庫中。這些工具可以把ORACLE的資料匯出到SQL Server,它們也一定會碰到這種問題,這樣的工具是如何解決這種問題。 ===================引 用 shunchia63 文 章=================== 你可以用分頁的SQL 解決 你懷疑的部份 例 Page1 :找1~1萬 Page2 :找1萬~2萬 ...... 1000萬要跑多久 ?? 1day ?? TxLog DB不會爆掉嗎 ?? |
sryang
尊榮會員 發表:39 回覆:762 積分:920 註冊:2002-06-27 發送簡訊給我 |
既然這樣的話,可能就要使用 server side cursor 了
請參考這兩篇: http://delphi.ktop.com.tw/board.php?cid=30&fid=66&tid=48618 http://delphi.ktop.com.tw/board.php?cid=30&fid=66&tid=50515 ===================引 用 carstyc 文 章=================== sryang兄您好: 感謝您的範例,但也許小弟描述的不夠清楚。 就以您的範例來說,我碰到的問題不在於Query2塞資料,Query2 Insert Data的部份我已經用其它方式處理掉了,這部份沒問題。 我的問題在於 Query1 ,如果它Select 的Table 有一仟萬筆資料時,可能執行 Query1.next 五十萬次後,Query1就會承受不了這麼大的資料量而『爆炸』了。也就是說,雖然Query1的cursor雖然停在第五十萬筆資料,但前面的四十九萬九仟九佰九十九筆資料都『暫存』在電腦的記憶體中。如此下來,就算電腦有再多的Memory,也不夠塞一仟萬筆資料。我該怎麼解決這個問題。 所以這就是我的問題所在,不知Sryang兄有了解了小弟的困擾了嗎? 以上說明,謝謝
------
歡迎參訪 "腦殘賤貓的備忘錄" http://maolaoda.blogspot.com/
編輯記錄
sryang 重新編輯於 2007-09-23 00:26:53, 註解 無‧
|
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
Sryang兄您好:
謝謝您的回覆,但我看了一下這兩篇文章,似乎都是在談如何接回Store Procedure的Cusror,不像是我的問題。 另 Server Side Cursor的方式我也有試過,但似乎沒有解決資料量大的問題,還是一樣會『爆炸』。 難道Data Dump的小工具真的這麼難寫嗎? ===================引 用 sryang 文 章=================== 既然這樣的話,可能就要使用 server side cursor 了 請參考這兩篇: http://delphi.ktop.com.tw/board.php?cid=30&fid=66&tid=48618 http://delphi.ktop.com.tw/board.php?cid=30&fid=66&tid=50515 |
bighm
一般會員 發表:5 回覆:21 積分:15 註冊:2006-10-29 發送簡訊給我 |
請教一下,您一定要使用delphi 達到此效果嗎?
如果不一定,小弟這裡有幾個小方法,您參考看看: 1. 利用類似 PLSQL Develop 或 toad 等工具,產出 sql 檔,如果沒有類似的工具,就用 spool 自己做出來 sql 檔 2. 利用 oracle 內的 spool,想辦法把 insert into 的語法做出來,方法如下: a. 進入sql*plus [code sql] set pagesize 50000 set linesize 1000 spool c:\insert.sql select 'INSERT INTO TABLE2 (F1, F2) VALUES ('''||F1||''','''||F2||''');' from table1 spool off [/code] 以上動作,可將 insert into 子句產出,剩下就不是問題了 ps. sppo的用途為將螢幕上的字元印到指定檔案中,所以產出來的檔案,請記得先把不要的字元清除掉 PPS. TADOQuery的設定,好像一定會暫存到記憶體中,您可以試試看 .NET 的 OLEDBDataReader搭配 OLEDBDataCommand 來試試看(不保証可行,因為我不曉得您的server 端的暫存區夠不夠撐得住, 不過我還是比較建用 pl/sql或 SQL*Lader 來處理
編輯記錄
bighm 重新編輯於 2007-09-23 19:58:17, 註解 無‧
|
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
Bighm兄您好:
您提的這個方法,其實我也有想過,只是前幾個回覆中有提過,這樣的Table有幾十個至幾佰個。如果每個都要去整理Insert的語法。其實也是蠻花時間的。 而且我有試過用Toad去匯出資料到文字檔,它的方法其實很類似您說的方法,只是出來的 .sql 檔案曾經高達4G多,然後Toad也『當掉』了,作業系統也掛了,只能重開機。SQLPlus我倒是沒試過,我會試試看,不過我想失敗的機會應該也蠻高的。 至於 .NET 的開發環境我目前沒有,有機會我再想辦法弄來試看看。但是我還是比較想知道,難道DELPHI的TQuery或TADOQuery碰到筆數大的TABLE,真的只能放棄嗎?用DELPHI真的寫不出資料Export的工具嗎? 那位有經驗的高高手,幫個忙解惑一下吧。 ===================引 用 bighm 文 章=================== 請教一下,您一定要使用delphi 達到此效果嗎? 如果不一定,小弟這裡有幾個小方法,您參考看看: 1. 利用類似 PLSQL Develop 或 toad 等工具,產出 sql 檔,如果沒有類似的工具,就用 spool 自己做出來 sql 檔 2. 利用 oracle 內的 spool,想辦法把 insert into 的語法做出來,方法如下: a. 進入sql*plus [code sql] set pagesize 50000 set linesize 1000 spool c:\insert.sql select 'INSERT INTO TABLE2 (F1, F2) VALUES ('''||F1||''','''||F2||''');' from table1 spool off [/code] 以上動作,可將 insert into 子句產出,剩下就不是問題了 ps. sppo的用途為將螢幕上的字元印到指定檔案中,所以產出來的檔案,請記得先把不要的字元清除掉 PPS. TADOQuery的設定,好像一定會暫存到記憶體中,您可以試試看 .NET 的 OLEDBDataReader搭配 OLEDBDataCommand 來試試看(不保証可行,因為我不曉得您的server 端的暫存區夠不夠撐得住, 不過我還是比較建用 pl/sql或 SQL*Lader 來處理 |
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
不要妄想用一個 TQuery 來處理超大量的資料量,越多的欄位越是不可能的任務,特別是有備註欄位的資料表更慘。
你可以讓撈資料的架構作個改變來降低對記憶體的需求 假定你的三個欄位都是 int 型態然後有一個主鍵欄位 當你用一個 TQuery 來讀取一百萬筆資料全部需要的記憶體 = 一百萬 x 3 x int 欄位所需的記憶體(可能是 32bit) 改用兩個 TQuery 來處理資料來源,例如 qyList 來做資料索引只讀取主鍵欄位 qyRow 來讀取完整的資料列(依據 qyList 的紀錄位置) 這樣對記憶體的最大需求在 qyList 上就是原來單一 TQuery 的三分之一記憶體需求 至於 qyRow 只會需要一筆紀錄的記憶體需求可以忽略不計 大致程式碼結構如下 [code delphi] procedure TForm1.FormCreate(Sender: TObject); begin qyList.SQL.Add('select 主鍵欄 from tbname'); qyRow.SQL.Add('select * from tbname where 主鍵欄 = :F1'); end; procedure TForm1.qyListBeforeScroll(DataSet: TDataSet); begin qyRow.Close; end; procedure TForm1.qyListAfterScroll(DataSet: TDataSet); begin qyRow.Open; end; procedure TForm1.qyRowBeforeOpen(DataSet: TDataSet); begin qyRow.ParamByName('F1').AsString := qyList.FieldByName('主鍵欄').AsString; end; procedure TForm1.btnCopyDataClick(Sender: TObject); begin qyList.Open; while not qyList.Eof do begin qyDist.ParamByName('FIELD1').Value := qyRow.FieldByName('FIELD1').Value; qyDist.ParamByName('FIELD2').Value := qyRow.FieldByName('FIELD2').Value; qyDist.ParamByName('FIELD3').Value := qyRow.FieldByName('FIELD3').Value; qyDist.ExecSql; qyList.Next; end; qyList.Close; end; [/code] 這樣的處理技巧相信可以解決你的問題
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
Bestlong兄您好:
感謝您的指點,我有懂您在說什麼,這樣也許是個方法,我會去試試。 但我個人是比較好奇想知道,一般的Dump工具也會是用如此方式處理嗎,這樣變成要先去找到適當的『Key』值,才有辦法使用Bestlong兄您說的方法,只是一般這種工具軟體有辦法正確的去得知適當的Key值嗎?還是他們用其他的怪方法。 各位大大請勿見怪,覺得小弟怎麼吹毛求疵,小弟只是好奇,市面上好用的Data Dump工具它們怎麼處理這樣的問題。這個結果也許會讓很多人都學習到不一樣的『進階』技巧。 以上說明,謝謝 ===================引 用 bestlong 文 章=================== 不要妄想用一個 TQuery 來處理超大量的資料量,越多的欄位越是不可能的任務,特別是有備註欄位的資料表更慘。 你可以讓撈資料的架構作個改變來降低對記憶體的需求 假定你的三個欄位都是 int 型態然後有一個主鍵欄位 當你用一個 TQuery 來讀取一百萬筆資料全部需要的記憶體 = 一百萬 x 3 x int 欄位所需的記憶體(可能是 32bit) 改用兩個 TQuery 來處理資料來源,例如 qyList 來做資料索引只讀取主鍵欄位 qyRow 來讀取完整的資料列(依據 qyList 的紀錄位置) 這樣對記憶體的最大需求在 qyList 上就是原來單一 TQuery 的三分之一記憶體需求 至於 qyRow 只會需要一筆紀錄的記憶體需求可以忽略不計 大致程式碼結構如下 [code delphi] procedure TForm1.FormCreate(Sender: TObject); begin qyList.SQL.Add('select 主鍵欄 from tbname'); qyRow.SQL.Add('select * from tbname where 主鍵欄 = :F1'); end; procedure TForm1.qyListBeforeScroll(DataSet: TDataSet); begin qyRow.Close; end; procedure TForm1.qyListAfterScroll(DataSet: TDataSet); begin qyRow.Open; end; procedure TForm1.qyRowBeforeOpen(DataSet: TDataSet); begin qyRow.ParamByName('F1').AsString := qyList.FieldByName('主鍵欄').AsString; end; procedure TForm1.btnCopyDataClick(Sender: TObject); begin qyList.Open; while not qyList.Eof do begin qyDist.ParamByName('FIELD1').Value := qyRow.FieldByName('FIELD1').Value; qyDist.ParamByName('FIELD2').Value := qyRow.FieldByName('FIELD2').Value; qyDist.ParamByName('FIELD3').Value := qyRow.FieldByName('FIELD3').Value; qyDist.ExecSql; qyList.Next; end; qyList.Close; end; [/code] 這樣的處理技巧相信可以解決你的問題 |
bestlong
站務副站長 發表:126 回覆:734 積分:512 註冊:2002-10-19 發送簡訊給我 |
我這裡所提供的方法,僅能處理有唯一值欄位資料表的狀況。
若是 datadump 工具可就沒這麼單純,還必須可以處理沒有唯一值欄位資料表的狀況。 如果你只是使用 TQuery 這個元件來處理以及思考,就會造成你處理的方式受限於元件的特性。 TQuery 讀取到的資料都會 Cache 在記憶體中, 某方面是為了讓資料可以雙向操作也就是可以由 First 到 Last 還可以由 Last 到 First 的移動。 而 Dump 的思考就只需要單向操作由 First 到 Last 移動,而且還可以用過就丟棄, 所以使用 TQuery 來處理 Dump 只是一個可能解卻不是一個完美解, 在少量資料環境下會正常運作,可是當碰到超大量資料的環境下就難說了, 總之電腦再怎麼發達或是高檔絕對都是一個有限資源的運作環境, 做好有限資源的運用管理,才能更加提昇軟體的品質。
------
http://blog.bestlong.idv.tw/ http://www.bestlong.idv.tw/ http://delphi-ktop.bestlong.idv.tw/ |
sryang
尊榮會員 發表:39 回覆:762 積分:920 註冊:2002-06-27 發送簡訊給我 |
所以,直接呼叫 BDE API 來取得資料,就沒有暫存資料的問題了。樓主可以試試看
以下程式是參考 BDE.HLP 裡面的範例寫的,確定可以執行 [code delphi] procedure TForm1.Button1Click(Sender: TObject); var hDB: hDBIDb; // Database handle hCur: hDBICur; // Handle to the data cursor szTableName: array [0..DBIMAXNAMELEN] of Char; // Table name CursorProps: CURProps; // Data cursor properties RecordBuffer: pBYTE; // Buffer into which to load a record FieldBuffer: array [0..2047] of Char; // Variable for a field IsBlank: BOOL; // Variable for blank fields EndOfFile: DBIResult; i: integer; s: string; begin // 初始 BDE Check(DbiInit(nil)); // 連接資料庫 Check(DbiOpenDatabase('BDE ALIAS NAME', 'ORACLE', dbiREADONLY, dbiOPENSHARED, '密碼', 0, nil, nil, hDB)); // 開啟 Table szTableName := 'TABLE1'; Check(DbiOpenTable(hDB, szTableName, szPARADOX, nil, nil, 0, dbiREADONLY, dbiOPENSHARED, xltFIELD, False, nil, hCur)); // 取得 Cursor 屬性 Check(DbiGetCursorProps(hCur, CursorProps)); // 配置一塊可以放得下一筆紀錄的記憶體 RecordBuffer := AllocMem(CursorProps.iRecBufSize * SizeOf(BYTE)); // 移動到第一筆 Check(DbiSetToBegin(hCur)); // 讀取一筆 EndOfFile := DbiGetNextRecord(hCur, dbiNOLOCK, RecordBuffer, nil); while EndOfFile = DBIERR_NONE do begin s := ''; // 讀取各欄位資料 for i := 1 to CursorProps.iFields do begin // 檢查欄位是否為 NULL Check(DbiGetField(hCur, i, RecordBuffer, nil, IsBlank)); if not IsBlank then begin // 讀取欄位資料 Check(DbiGetField(hCur, i, RecordBuffer, pBYTE(@FieldBuffer), IsBlank)); s := s StrPas(FieldBuffer) #9 end else s := s '(null)' #9 end; // 輸出到 Memo Memo1.Lines.Add(s); // 讀取下一筆 EndOfFile := DbiGetNextRecord(hCur, dbiNOLOCK, RecordBuffer, nil); end; // 釋放紀錄緩衝區 FreeMem(RecordBuffer); // 關閉游標 if not (hCur = nil) then Check(DbiCloseCursor(hCur)); // 關閉資料庫連接 if not (hDB = nil) then Check(DbiCloseDatabase(hDB)); // 釋放 BDE Check(DbiExit); end; [/code]
------
歡迎參訪 "腦殘賤貓的備忘錄" http://maolaoda.blogspot.com/
編輯記錄
sryang 重新編輯於 2007-09-26 12:51:11, 註解 無‧
|
carstyc
資深會員 發表:16 回覆:254 積分:329 註冊:2003-07-18 發送簡訊給我 |
|
helper197
一般會員 發表:8 回覆:10 積分:3 註冊:2008-08-20 發送簡訊給我 |
請教bestlong Sir 這段程式
該如何用BCB表達呢?? procedure TForm1.qyRowBeforeOpen(DataSet: TDataSet); begin qyRow.ParamByName('F1').AsString := qyList.FieldByName('主鍵欄').AsString; end; 主要是這段 我看不懂 整段程序內 不需要qyRow.Next(); 嗎?? 還是說只要 透過 procedure TForm1.qyRowBeforeOpen(DataSet: TDataSet); qyList.Next 則 qyRow 自己就會 Next呢?? 謝謝您 ===================引 用 bestlong 文 章=================== 不要妄想用一個 TQuery 來處理超大量的資料量,越多的欄位越是不可能的任務,特別是有備註欄位的資料表更慘。 你可以讓撈資料的架構作個改變來降低對記憶體的需求 假定你的三個欄位都是 int 型態然後有一個主鍵欄位 當你用一個 TQuery 來讀取一百萬筆資料全部需要的記憶體 = 一百萬 x 3 x int 欄位所需的記憶體(可能是 32bit) 改用兩個 TQuery 來處理資料來源,例如 qyList 來做資料索引只讀取主鍵欄位 qyRow 來讀取完整的資料列(依據 qyList 的紀錄位置) 這樣對記憶體的最大需求在 qyList 上就是原來單一 TQuery 的三分之一記憶體需求 至於 qyRow 只會需要一筆紀錄的記憶體需求可以忽略不計 大致程式碼結構如下 [code delphi] procedure TForm1.FormCreate(Sender: TObject); begin qyList.SQL.Add('select 主鍵欄 from tbname'); qyRow.SQL.Add('select * from tbname where 主鍵欄 = :F1'); end; procedure TForm1.qyListBeforeScroll(DataSet: TDataSet); begin qyRow.Close; end; procedure TForm1.qyListAfterScroll(DataSet: TDataSet); begin qyRow.Open; end; procedure TForm1.qyRowBeforeOpen(DataSet: TDataSet); begin qyRow.ParamByName('F1').AsString := qyList.FieldByName('主鍵欄').AsString; end; procedure TForm1.btnCopyDataClick(Sender: TObject); begin qyList.Open; while not qyList.Eof do begin qyDist.ParamByName('FIELD1').Value := qyRow.FieldByName('FIELD1').Value; qyDist.ParamByName('FIELD2').Value := qyRow.FieldByName('FIELD2').Value; qyDist.ParamByName('FIELD3').Value := qyRow.FieldByName('FIELD3').Value; qyDist.ExecSql; qyList.Next; end; qyList.Close; end; [/code] 這樣的處理技巧相信可以解決你的問題 |
小傑克
資深會員 發表:5 回覆:209 積分:357 註冊:2009-02-16 發送簡訊給我 |
如果樓上的問題只是在用TAdoQuery讀取大量資料的話,可以試試把 TAdoQuery的屬性CursorLocation 設定成clUseServer 這就可以把DataSet裡面ADOQuery1.IsSequenced 這個屬性變成false 也就是叫delphi 不要自己保留buffer去做RecNo 這樣就不會有讀到爆的問題,而且也不會有越讀越慢的狀況
保證讀取非常快
------
額有朝天骨,眼中有靈光
編輯記錄
小傑克 重新編輯於 2009-02-16 14:50:20, 註解 無‧
|
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |