不知怎麼了,每次寫ORACLE存取程式都會在咒罵中度過"美好"時光,剛才花了半小時處理一個錯誤:

ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'MYSTOREDPROC'

程式碼差不多像以下這個樣子,要傳入十五個名稱又臭又長的參數給一個Procedure:

OracleCommand cmd = new OracleCommand("MyStoredProc");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("p_complex_para_name", p1);
cmd.Parameters.AddWithValue("p_more_complex_para_name", p2);
//...
cmd.ExecuteNonQuery();

錯誤指出與參數數目或型別有關,p1, p2...清一色是string,而所有Procedure參數都是VARCHAR2,檢查過所有OracleParameter.DbType都是AnsiString,因此我一口咬定是數目或名稱出了問題。於是名稱比了又比,數目數了又數,就是找不出問題出在哪裡。(還無意中學到一個Tip,ORACLE的參數名詞如果超過31個字母,也會導致以上錯誤)

最後把仔細檢查了p1-p15,發現其中有一個的值是null!!

換句話說,Parameters.AddWithValue("..",  null)時,即使OracleParameter.DbType == DbType.AnsiString也會導致傳送到ORACLE的參數型別不正確。如果擔心這類問題,避用AddWithValue,改用Add明確指定型別,也是不錯的寫法。

特貼文一篇紀念本次鬼打牆!!


Comments

# by Jasper

假設P1是字串,我會先判p1是否為空值(String.isNullorEmpty),若是則 cmd.Parameters.AddWithValue("p_complex_para_name", DBNull.Value); 若是型別,我會先判p1是否為Nothing,然後以此類推,不過以上情境目前我是用於SQL,也就是說DBNull 是否能被Oracle所支援,我還沒做過實驗,一點小想法...見笑了...

# by tom

雖然我沒有用過 Oracle 的經驗, 但我客戶一直想把現在使用 SQL server 2000/2005 的 AP 轉換到 Oracle 上... 看到您這邊常分享些 Oracle 的午夜怪談, 總好奇為何大老闆都偏好 Oracle, 真的有比較好用嗎?

# by Jeffrey

to Jasper, 當初用AddWithValue是偷懶想省下宣告型別的功夫,與其先判斷IsNulllOrEmpty,我想我會寫成Add("fld_name", OracleType.VarChar).Value = p1吧。 to tom, 與其說大老板都用ORACLE,不如說主事者最後選了ORACLE。業界普遍認為ORACLE最經打耐操(是否屬實? 就只有天知道了),執意要用其他牌DB,萬一有什麼閃失,難保不會有人補刀: "早就叫你用ORACLE,你偏不要..."。Nobody get fired for buying ORACLE,哈!

# by Wizard

請問一下 明儒老師, 像在 SQL Server 中,若用最高權限的 sa 登入,並建立一個資料表 table1, 則視為是 dbo.table1, 以後當應用程式或其他使用者存取時,直接用 : select * from table1 即可, 前面不需要加 dbo 或建立資料表的帳號名稱,亦即不需要用到下列語句 : select * from 建立者帳號.table1 select * from dbo.table1 但 Oracle 好像不能這樣, Oracle 的設計較特殊,某個使用者建立的資料表,就只屬於他自己的 Schema,而非共用的資料表。 因此當別的使用者要撈取該資料表時, 前面好像一定要強制加上「建立資料表的帳號.」, 因此造成小弟我的困擾。 即使用最高權限的 Sys 和 System 帳號建立的資料表 table1, 要讓應用程式撈取時,也不能直接下 : select * from table1; 資料表前面一定要加建立者的名稱才行,如 : select * from sys.table1; select * from system.table1; 即使用 Sys 和 System 帳號登入,要 select 別的帳號建的資料表, 也一定要在資料表前加上「建立者帳號.」, 才能存取資料表。 但這樣造成小弟我以前用 SQL Server、Sybase 等資料庫, 所建立的許多 SQL 語法、應用程式,無法順利轉移到 Oracle 中, 亦即所有的資料表在 Oracle 中撈取時, 前面都要加上「sys.」或「建立資料表的帳號.」, 這樣真的會改到死。 請問老師,能否提供一些提示和加持, 大概該怎麼做? 或 google 可用的查詢關鍵字組合? 謝謝。

# by Jeffrey

to Wizard, 你可以為這些跨Schema的Table建一個Synonym,我找到一篇不錯的中文說明: http://www.rsa.idv.tw/?p=115

# by Wizard

已解決,感謝。 建資料表的用戶、程式連線的用戶,都用同一人即可。 或用「別名」: create public synonym t001 for 用戶名.t01; drop public synonym t001;

# by WizardWu

我用 Add 方法也有一樣的問題,當參數值為 null 時,當要寫入 NVarchar2 的欄位時,丟出錯誤,說參數未被賦予值,如下 : string strParam1 = null; odCmd.Parameters.Add(":DESCP", OracleType.NVarChar, 100).Value = strParam1; 若用 OleDb 則不會,要用 OracleClient 才會。 只好在 Oracle 每個可為 Null 的欄位,在寫入值時,都先用程式判斷,要寫入的值是否為 Null,如下 : if (string.IsNullOrEmpty(strParam1)) odCmd.Parameters.Add(":DESCP", OracleType.NVarChar, 100).Value = DBNull.Value; else odCmd.Parameters.Add(":DESCP", OracleType.NVarChar, 100).Value = strParam1;

# by Jasper

WizardWu ...我的解法與您相同...呵~~~,真的沒比較人性點的寫法嗎?

Post a comment