2011-05-31 80 views
2

我想創建一個Oracle包,我有一個執行一些動態SQL的過程。如果我使用EXECUTE IMMEDIATE來完成動態調整,這是沒有問題的,但如果查詢的靜態部分可以編碼爲靜態(以便進行編譯時檢查)會更好。全動態查詢可能使用動態部分擴展靜態SQL語句嗎?

例子:什麼,我試圖讓

-- v_stmt is built dynamically. 
v_stmt := 'SELECT count(*) FROM <here some joins> WHERE <here some conditions>'; 
EXECUTE IMMEDIATE v_stmt 
    USING v_param1, v_param2 
    RETURNING INTO v_count; 

例的FROM部分靜態:

-- v_stmt is built dynamically. 
v_stmt := 'SELECT count(*) FROM my_package.my_function(:param1, :param2) WHERE <here some conditions>'; 
EXECUTE IMMEDIATE v_stmt 
    USING v_param1, v_param2 
    RETURNING INTO v_count; 

FUNCTION my_function(
    i_param1 IN VARCHAR2, 
    i_param2 IN NUMBER 
) 
RETURN SYS_REFCURSOR 
AS 
    v_cursor SYS_REFCURSOR; 
BEGIN 
    -- Open a cursor for different queries depending on params. 
    IF i_param2 = 1 THEN 
     OPEN v_cursor FOR <some static query>; 
    ELSE 
     OPEN v_cursor FOR <some other static query>; 
    END IF; 
    RETURN v_cursor; 
END; 

這不起作用,因爲它不可能從選擇SYS_REFCURSOR(至少這是我在Google上找到的)。

有什麼辦法可以達到這個目標嗎?

編輯:按照要求,這裏有一些例子:

靜態查詢:

SELECT a.*, ca.CUS_ID FROM adresses a INNER JOIN customer_adresses ca ON (ca.adr_id = a.adr_id); 
SELECT p.*, cp.CUS_ID FROM persons p INNER JOIN customer_persons cp ON (cp.per_id = p.per_id); 

然後,他們被動態延伸,就像下面的例子:

-- Checks if there is an adress in the customer where the zip is null. 
SELECT count(*) FROM <static adresses query> q WHERE q.cus_id = :param1 AND a.zip IS NULL; 
-- Checks if there is at least one person in the customer. 
SELECT count(*) FROM <static persons query> q WHERE q.cus_id = :param1; 
+1

根據您的*精確要求*,這可能會也可能不會。請給我們幹(靜態)查詢和一些動態擴展的具體例子。 – APC 2011-05-31 18:59:11

+0

@APC我已經添加了一些例子。 – cremor 2011-06-01 05:18:10

回答

1

很抱歉,但爲什麼需要這樣做?通過引入一個函數可以返回不同類型的數據/表,這取決於參數列表,似乎你已經完成了複雜的任務。非常令人困惑的imo。此外,你必須在某處做這項工作,你只是試圖隱藏它在這個功能(如果param1 = this,那麼x如果param1 =然後y ...)

此外,即使你已經實現一個遊標函數(甚至是流水線),在這種情況下這將是一個糟糕的主意,因爲你會迫使Oracle做一些它不一定需要做的工作(現在忽略所有的上下文切換)。爲了得到一個計數,你可以讓Oracle抓取每一行結果並計數。很多時候,Oracle可以快速完成索引掃描以獲取計數(取決於當然的查詢)。而且如果在緩衝區緩存中找到塊,通常多次運行相同的查詢將不需要每次都完成所有工作。我會挑戰你使用直接的SQL多次使用返回遊標的函數來運行計數。你可能會感到驚訝。據我所知(關於這一點,請參閱我),新的11g函數結果緩存不適用於流水線函數或返回引用遊標的函數(以及由於依賴於表的其他問題,如失效)。所以,我說的是爲什麼不只是做:select count(1)into v_variable from ...;

如果你想隱藏和模塊化,那麼只要知道你可能會失去什麼。

+0

你說得對,只要有編譯時間檢查我的語句就複雜得多。我現在已經將所有內容都改爲完全動態的,並將使用單元測試來檢查查詢。 – cremor 2011-06-01 15:34:06

1

您可能希望在function1中打開一個查詢,然後將它的結果作爲一個表傳遞給function2,然後將函數2添加一個where子句「表」

在這種情況下,你會想重寫功能1爲pipelined table function

v_stmt := 'SELECT count(*) FROM table(my_package.my_function(:param1, :param2)) WHERE <here some conditions>'; 
EXECUTE IMMEDIATE v_stmt 
    USING v_param1, v_param2 
    RETURNING INTO v_count; 

CREATE TYPE object_row_type AS OBJECT (
    OWNER    VARCHAR2(30), 
    OBJECT_TYPE  VARCHAR2(18), 
    OBJECT_NAME  VARCHAR2(30), 
    STATUS    VARCHAR2(7) 
); 


CREATE TYPE object_table_type AS TABLE OF object_row_type; 


FUNCTION my_function(
    i_param1 IN VARCHAR2, 
    i_param2 IN NUMBER 
) 
RETURN object_table_type PIPELINED AS 
BEGIN 
+0

我的函數(包含靜態查詢)返回不同類型的表(例如:customers,projects,persons,...)。我不認爲這將與對象表一起工作,因爲對象具有固定的結構,對嗎? – cremor 2011-06-01 05:23:25

+0

是的,但你不必從你的表函數返回固定類型。 「Oracle有三種特殊的SQL數據類型,可以動態地封裝和訪問任何其他SQL類型(包括對象和集合類型)的類型描述,數據實例和一組數據實例,還可以使用這三種特殊類型創建匿名也就是未命名的)類型,包括匿名集合類型,類型爲SYS.ANYTYPE,SYS.ANYDATA和SYS.ANYDATASET。在某些情況下,SYS.ANYDATA類型可用作表函數的返回值。 – bpgergo 2011-06-01 08:32:05

+0

from [Oracle docs](http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/tuning.htm#i52932) – bpgergo 2011-06-01 08:33:35

0

您可以使用Oracle expression filter進行編譯時檢查表達式。

這可能比其他解決方案更復雜,但如果您確實需要驗證自己的條件,則可能會有幫助。