在使用ODP.NET進行Oracle編程時,有時候SQL語句非常複雜,需要採用動態構造查詢語句的情況,有兩種方法可以構造動態SQL語句,並執行返回結果集。
1、在資料訪問層構造SQL語句
例如下面的語句,將構造完整的SQL語句賦值給CommandText,再傳遞到資料庫進行執行,返回結果集。
loadCommand.CommandType = CommandType.Text
loadCommand.CommandText = "Select * From Users"
dataAdapter .SelectCommand = loadCommand
dataAdapter . Fill(data)
dataAdapter .SelectCommand = loadCommand
dataAdapter . Fill(data)
該方法需要將整個SQL的構造過程放在DataAccess層,商務邏輯發生變化,修改不方便,而且每次查詢需要傳遞給資料庫很長的查詢字串,傳遞參數的效率也不高。
2、在預存程序中構造動態SQL語句並執行
以下為一個完整的案例(經過刪減),其中RefCursor 為自訂遊標類型
PROCEDURE G_Search(P_YearNO IN NUMBER,
P_ControlType IN NUMBER,
P_Progress IN CHAR,
P_DepartID IN VARCHAR2,
P_ProjectName IN NVARCHAR2,
C_Projects OUT RefCursor) IS
e_ErrInterruption EXCEPTION;
v_ErrID NUMBER; --Variable to hold the errorlog id
v_ErrCode NUMBER; --Variable to hold the error message code
v_ErrText VARCHAR2(512); --Variable to hold the error message text
v_ErrProc VARCHAR2(50) := 'G_Search';
v_DepartID VARCHAR2(16);
v_ProjectName NVARCHAR2(128);
v_SQL VARCHAR2(512);
v_Where VARCHAR2(256);
BEGIN
v_SQL := 'SELECT PROJECTID, PARENTID, PROJECTNAME ';
v_SQL := v_SQL || ' FROM PROJECTS A';
v_Where := ' Where';
-- 年度
IF P_YearNO < 9999 THEN
v_Where := v_Where || ' A.YearNO = ' || P_YearNO || ' And';
ELSE
v_Where := v_Where || ' A.YearNO < ' || P_YearNO || ' And';
END IF;
-- 控制類別
IF P_ControlType = 9 THEN
v_Where := v_Where || ' A.ControlType < 9 And';
ELSE
v_Where := v_Where || ' A.ControlType = ' || P_ControlType ||
' And';
END IF;
-- 進度
IF P_Progress < 'Z' THEN
v_Where := v_Where || ' A.Progress = ''' || P_Progress || ''' And';
ELSE
v_Where := v_Where || ' A.Progress < ''' || P_Progress || ''' And';
END IF;
IF TRIM(P_DepartID) <> '%' THEN
v_Where := v_Where || ' A.DepartID = ''' || P_DepartID || ''' And';
ELSE
v_Where := v_Where || ' A.DepartID Like ''' || P_DepartID ||
''' And';
END IF;
--項目名稱
v_ProjectName := NVL(P_ProjectName,
'%');
IF v_ProjectName <> '%' THEN
v_ProjectName := '%' || P_ProjectName || '%';
END IF;
v_Where := v_Where || ' A.ProjectName Like ' || '''' || v_ProjectName ||
''' And';
v_SQL := v_SQL || v_Where;
OPEN C_PROJECTS FOR v_SQL;
--COMMIT;
EXCEPTION
--根據需要定義錯誤異常
WHEN OTHERS THEN
--ROLLBACK;
v_ErrID := SQLCODE;
v_ErrText := SQLERRM;
raise_application_error(v_ErrID,
v_ErrText);
END G_Search;
該方法只需要傳遞給預存程序一些參數,使用遊標返回資料。參數傳遞效率較高,而且商務邏輯在預存程序中,調整比較方便。該方法關鍵的在下面的語句:
Open C_Projects For v_SQL;
它直接使用遊標開啟構造的查詢字串即可。
注意事項:
A)、構造的SQL語句最後不能帶有分號;
B)、SQL語句中對於字元和字串的條件需要用單引號包括起來
C)、最重要:動態SQL語句需要防止SQL注入攻擊。我們採用最簡單的辦法,只允許一個關鍵詞查詢,將關鍵詞中的所有空格去掉。對於多關鍵詞,需要將他們用空格拆開,再構造。