2012-06-12 29 views
6

Tôi đã một stored procedure mà chấp nhận nhiều thông số (ví dụ pName, pHeight, pTeam)thông số Binding để Oracle SQL động

Tôi có truy vấn xây dựng như thế này:

SQLQuery VARCHAR2(6000); 
TestCursor T_CURSOR; 

SQLQuery := 'SELECT ID, Name, Height, Team FROM MyTable WHERE ID IS NOT NULL '; 


-- Build the query based on the parameters passed. 
IF pName IS NOT NULL 
    SQLQuery := SQLQuery || 'AND Name LIKE :pName '; 
END IF; 

IF pHeight IS > 0 
    SQLQuery := SQLQuery || 'AND Height = :pHeight '; 
END IF; 

IF pTeam IS NOT NULL 
    SQLQuery := SQLQuery || 'AND Team LIKE :pTeam '; 
END IF; 


OPEN TestCursor FOR SQLQuery USING pName, pHeight, pTeam; 

Nếu tôi thực hiện thủ tục chuyển tất cả các tham số, nó chạy đúng cách.

Nhưng nếu tôi chỉ thông qua một hoặc hai trong số các thông số, sau đó các lỗi thủ tục ra:

ORA-01006: bind variable does not exist 

Làm thế nào để chọn lọc ràng buộc biến với các thông số dựa trên nơi giá trị tham số được sử dụng? Ví dụ, nếu chỉ pName được thông qua, sau đó tôi sẽ chỉ thực hiện truy vấn:

OPEN TestCursor FOR SQLQuery USING pName; 

Hoặc nếu cả hai pName và pTeam được thông qua, sau đó:

OPEN TestCursor FOR SQLQuery USING pName, pTeam; 

Hope ai đó có thể đổ nhiều cách để giải quyết điều này. Cảm ơn.

Edit: tôi thực sự có thể sử dụng như sau:

- Xây dựng các truy vấn dựa trên các thông số thông qua. NẾU pName KHÔNG phải là NULL SQLQuery: = SQLQuery || 'VÀ Tên NHƯ' '' || pName || '' ''; END IF;

IF pHeight IS > 0 
    SQLQuery := SQLQuery || 'AND Height = pHeight '; 
END IF; 

IF pTeam IS NOT NULL 
    SQLQuery := SQLQuery || 'AND Team LIKE ''' || pTeam || ''' '; 
END IF; 


OPEN TestCursor FOR SQLQuery; 

Nhưng điều này sẽ rất dễ bị SQL Injection ...

Trả lời

6

Đây không phải là vô cùng tao nhã nhưng nó có nghĩa là bạn luôn luôn có thể cung cấp tất cả ba biến ràng buộc ngay cả khi một số trong số đó là null. Bạn chỉ thêm các mệnh đề thêm WHERE nếu cần.

(Tôi đã cố gắng định dạng SQL động để làm cho nó dễ đọc hơn, bạn chỉ có thể cung cấp nó dưới dạng một chuỗi dài).

FUNCTION myFunc (
    pName IN VARCHAR2, 
    pHeight IN VARCHAR2, 
    pTeam IN VARCHAR2 
) 
    RETURN T_CURSOR 
IS 
    -- Local Variables 
    SQLQuery VARCHAR2(6000); 
    TestCursor T_CURSOR; 
BEGIN 
    -- Build SQL query 
    SQLQuery := 'WITH t_binds '|| 
       ' AS (SELECT :v_name AS bv_name, '|| 
          ' :v_height AS bv_height, '|| 
          ' :v_team AS bv_team '|| 
         ' FROM dual) '|| 
       ' SELECT id, '|| 
         ' name, '|| 
         ' height, '|| 
         ' team '|| 
       ' FROM MyTable, '|| 
         ' t_binds '|| 
       ' WHERE id IS NOT NULL'; 

    -- Build the query WHERE clause based on the parameters passed. 
    IF pName IS NOT NULL 
    THEN 
    SQLQuery := SQLQuery || ' AND Name LIKE bv_name '; 
    END IF; 

    IF pHeight > 0 
    THEN 
    SQLQuery := SQLQuery || ' AND Height = bv_height '; 
    END IF; 

    IF pTeam IS NOT NULL 
    THEN 
    SQLQuery := SQLQuery || ' AND Team LIKE bv_team '; 
    END IF; 

    OPEN TestCursor 
    FOR SQLQuery 
    USING pName, 
     pHeight, 
     pTeam; 

    -- Return the cursor 
    RETURN TestCursor; 
END myFunc; 

Tôi không phải là ở phía trước của một máy trạm với truy cập DB vì vậy tôi không thể kiểm tra chức năng nhưng nó phải là gần (xin vui lòng tha thứ cho bất kỳ lỗi cú pháp, nó là một ngày dài!)

Hy vọng nó sẽ giúp ...

+0

T_binds được khai báo ở đâu? Hay tôi cần khai báo ở đâu đó? – Batuta

+0

Nó được khai báo trong mệnh đề 'WITH' trong câu lệnh SQL, nó là một bảng giả để giữ các biến liên kết. Xem tại đây: http://www.orafaq.com/node/1879 – Ollie

+0

Đã thử điều này nhưng vẫn nói ORA-01008: không phải tất cả các biến bị ràng buộc – Batuta

7

Bạn có thể sử dụng gói DBMS_SQL. Điều này cung cấp một cách khác để chạy SQL động. Nó có lẽ hơi cồng kềnh hơn để sử dụng, nhưng nó có thể linh hoạt hơn, đặc biệt là với các số tham số liên kết khác nhau.

Đây là cách bạn có thể sử dụng nó (cảnh báo: Tôi đã không kiểm tra này):

FUNCTION player_search (
    pName  IN VARCHAR2, 
    pHeight  IN NUMBER, 
    pTeam  IN VARCHAR2 
) RETURN SYS_REFCURSOR 
IS 
    cursor_name INTEGER; 
    ignore  INTEGER; 
    id_var  MyTable.ID%TYPE; 
    name_var  MyTable.Name%TYPE; 
    height_var MyTable.Height%TYPE; 
    team_var  MyTable.Team%TYPE; 
BEGIN 
    -- Put together SQLQuery here... 

    -- Open the cursor and parse the query   
    cursor_name := DBMS_SQL.OPEN_CURSOR; 
    DBMS_SQL.PARSE(cursor_name, SQLQuery, DBMS_SQL.NATIVE); 

    -- Define the columns that the query returns. 
    -- (The last number for columns 2 and 4 is the size of the 
    -- VARCHAR2 columns. Feel free to change them.) 
    DBMS_SQL.DEFINE_COLUMN(cursor_name, 1, id_var); 
    DBMS_SQL.DEFINE_COLUMN(cursor_name, 2, name_var, 30); 
    DBMS_SQL.DEFINE_COLUMN(cursor_name, 3, height_var); 
    DBMS_SQL.DEFINE_COLUMN(cursor_name, 4, team_var, 30); 

    -- Add bind variables depending on whether they were added to 
    -- the query. 
    IF pName IS NOT NULL THEN 
    DBMS_SQL.BIND_VARIABLE(cursor_name, ':pName', pName); 
    END IF; 

    IF pHeight > 0 THEN 
    DBMS_SQL.BIND_VARIABLE(cursor_name, ':pHeight', pHeight); 
    END IF; 

    IF pTeam IS NOT NULL THEN 
    DBMS_SQL.BIND_VARIABLE(cursor_name, ':pTeam', pTeam); 
    END IF; 

    -- Run the query. 
    -- (The return value of DBMS_SQL.EXECUTE for SELECT queries is undefined, 
    -- so we must ignore it.) 
    ignore := DBMS_SQL.EXECUTE(cursor_name); 

    -- Convert the DBMS_SQL cursor into a PL/SQL REF CURSOR. 
    RETURN DBMS_SQL.TO_REFCURSOR(cursor_name); 

EXCEPTION 
    WHEN OTHERS THEN 
    -- Ensure that the cursor is closed. 
    IF DBMS_SQL.IS_OPEN(cursor_name) THEN 
     DBMS_SQL.CLOSE_CURSOR(cursor_name); 
    END IF; 
    RAISE; 
END; 

(Lưu ý:. DBMS_SQL.TO_REFCURSOR là mới trong Oracle 11g)

+0

+1, tôi thích sử dụng DBMS_SQL vì nó được "hỗ trợ" nhiều hơn nhưng liên quan nhiều hơn phương pháp tôi đã đề xuất. – Ollie

0

Làm thế nào về

SQLQuery := 'SELECT ID, Name, Height, Team FROM MyTable WHERE ID IS NOT NULL '; 

SQLQuery := SQLQuery || 'AND Name LIKE :pName '; 
SQLQuery := SQLQuery || 'AND Team LIKE :pTeam '; 
SQLQuery := SQLQuery || 'AND (Height = :pHeight OR :pHeight = 0)'; 

OPEN TestCursor FOR SQLQuery USING nvl(pName, '%'), nvl(pTeam, '%'), nvl(pHeight, 0), nvl(pHeight, 0); 

?

+0

như '%' không ảnh hưởng đến null. vì vậy nếu một số trường này là null, bạn sẽ bị sai –

1

Cách tiếp cận tôi sử dụng là bao gồm trong SQL động một trường hợp ELSE nêu rõ ngược lại của IF. Mã của bạn kiểm tra rằng pName không phải là null, vì vậy tôi sẽ thêm một mệnh đề để thử nghiệm truy vấn được tạo ra rằng pName IS Null. Bằng cách đó, bạn có thể chuyển các thông số giống nhau mỗi lần mà không ảnh hưởng đến kết quả của truy vấn.

SQLQuery VARCHAR2(6000); 
TestCursor T_CURSOR; 

SQLQuery := 'SELECT ID, Name, Height, Team FROM MyTable WHERE ID IS NOT NULL '; 


-- Build the query based on the parameters passed. 
IF pName IS NOT NULL 
    SQLQuery := SQLQuery || 'AND Name LIKE :pName '; 
ELSE 
    SQLQuery := SQLQuery || 'AND :pName IS NULL'; 
END IF; 

IF pHeight IS > 0 
    SQLQuery := SQLQuery || 'AND Height = :pHeight '; 
ELSE 
    SQLQuery := SQLQuery || 'AND :pHeight <=0 '; 
END IF; 

IF pTeam IS NOT NULL 
    SQLQuery := SQLQuery || 'AND Team LIKE :pTeam '; 
ELSE 
    SQLQuery := SQLQuery || 'AND :pTeam IS NULL'; 
END IF; 


OPEN TestCursor FOR SQLQuery USING pName, pHeight, pTeam;