2007/12/25

KB-程式存取大量檔案資料夾的注意事項

最近遇到一個  case ,有一個資料夾中有超過10萬個檔案,記得檔案總管要開啟一個資料夾中含有 2 萬個檔案時,就已經無法回應,更別說排序了。

以前比較常的解決方案是從 DB 取得檔名後,直接 MOVE 檔案。

但是…如果沒有留存一份在 DB 呢?

.net 中有一個 System.IO.Directory 物件,有一個 Method 叫做 GetFiles ,可以回傳 Files 的 String Array ,

看來是很好用,不過,面臨到超過10萬個檔案的資料夾,在還沒有將 String Array 回傳時,應該就會將記憶體吃到滿載,

還可能會被 .net CLR 認為是一個發狂吃記憶體的豬頭程式,另外速度之慢,去吃一頓飯回來還不見得跑得出來。

看了一下 MSDN 中高手 Stephen Toub 的 NETMatter 專欄,有一段寫到 Directory.GetFiles 的問題與解決方式(註一)

主要是改用 Windows API 中的 FindFirstFile() 取得目錄中的第一個檔案後,再呼叫 FindNextFile() 遞迴存取檔案。

在這個專欄中,也有範例程式可以下載(註二)

實際使用後發現速度飛快,1000倍的差距一點都不誇張 (Y)。

改寫也蠻快的,由於 Stephen 了一個 FileSearcher Class ,首先很完整的 copy 至我的專案,

將原本的 Directory.GetFiles 一行打死的動作,改為 Foreach + FileSearcher.GetFiles 即可。

case 就這樣子透用 Stephen 的幫忙,很快就可以 Close ,不過裡面有很多可以再追根究抵的,例如:

SafeFindHandler , C# iterator performance (註三) .... 有空再研究囉~(掩面哭逃)

 

註一:http://msdn.microsoft.com/msdnmag/issues/05/12/NETMatters/

註二:http://download.microsoft.com/download/2/e/9/2e9bde04-3af1-4814-9f1e-733f732369a3/NETMatters0512.exe

註三:Recursive iterator performance , Recursive iterator performance, part 2 , Recursive iterator performance, part 3

 

2007/12/21

KB-ORA-14551: cannot perform a DML operation inside a query

當建立 Oracle Function 時,Build/complier 都沒有問題,但是執行就有問題,而且只有在 Select 中執行才會有問題。

create or replace function ufn_test return number is
    begin
        insert into testlog (d) values ('test');
    return 1;
end;
 
--execute
select ufn_test() as a from dual;

會出現以下訊息…

ORA-14551: cannot perform a DML operation inside a query

You need to write a separate procedure/function for executing the DML because oracle does not support executing DML inside the function.

or

cannot perform a DDL, commit or rollback inside a query or DML

後來在網路上找到很多同病相憐的人,整理一下,如果 Function 使用在 Select 語法中就會出現類似的錯誤:

  • Have OUT or IN OUT parameters
  • Commit or roll back the current transaction, create a savepoint or roll back to a savepoint, or alter the session or the system. DDL statements implicitly commit the current transaction, so a user-defined function cannot execute any DDL statements.

交易通常有幾種等級(以 COM+舉例):

  • 停用(Disabled)
  • 不支援(Not Supported)
  • 支援(Supported)
  • 必要(Required)
  • 需要新增(RequiresNew)

在 Oracle Function 中如果遇到交易的狀況,唯一的解決方式就是"需要新增"(RequiresNew),另起一個 Transaction,

可以使用 PRAGMA AUTONOMOUS_TRANSACTION; 來做。

CREATE OR REPLACE FUNCTION ufn_test RETURN NUMBER IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
        INSERT INTO testlog(d) VALUES ('test');
    COMMIT;
    RETURN 1;
END;

 

問題就可以解決,不過要思考的地方是一致性的問題,如果會有這類的考量,建議改用 Store Procedure 來做會比較好。

才不會主程序沒有確認,在 Function 中就已經更新。

 

 

參考文章:

http://forums.oracle.com/forums/thread.jspa?threadID=598160&tstart=0

 

AUTONOMOUS_TRANSACTION Pragma

http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96624/13_elems3.htm#32732

2007/12/18

KB-Toad v9 執行 Explain Plan 出現 ORA-00905 訊息

toad v9 與以前的版本有不太一樣,依之前的設定 KB-TOAD 執行 Explain Plan 遇到 ORA-0240 訊息 並不能解決問題,

仍是會出現 ORA-00905 Missing Keyword 錯誤,

經過使用 SQL Monitor 追查 Toad.exe 所使用 SQL Statment ,並與 Toad v8 做比較發現(如圖)…

SNAG-12-18-00

Toad v9 竟然會多一個 PUBLIC ,後來在 View->Toad Options-> Oracle -> General 中,將 Explain Plan 的 Schema 欄位,從原本的 PUBLIC 改為空白。

SNAG-12-18-01

 

"叮咚!!",令人感動的執行計劃就跑出來囉~