9

複数のパラメーター(つまり、pName、pHeight、pTeam)を受け入れるストアドプロシージャがあります

クエリは次のように作成されています。

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;

すべてのパラメータを渡してプロシージャを実行すると、正しく実行されます。

しかし、パラメータの1つまたは2つだけを渡した場合、プロシージャはエラーになります。

ORA-01006: bind variable does not exist

パラメータ値が使用された場所に基づいて、変数をパラメータに選択的にバインドするにはどうすればよいですか?たとえば、pNameのみが渡された場合、クエリのみを実行します。

OPEN TestCursor FOR SQLQuery USING pName;

または、pNameとpTeamの両方が渡された場合、次のようになります。

OPEN TestCursor FOR SQLQuery USING pName, pTeam;

誰かがこれを解決するためのより多くの方法を捨てることができることを願っています。ありがとう。

編集:私は実際に以下を使用することができます:

-渡されたパラメータに基づいてクエリを作成します。pNameが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;

しかし、これはSQLインジェクションに対して非常に脆弱です...

4

4 に答える 4

10

DBMS_SQLパッケージを使用できます。これは、動的SQLを実行するための代替方法を提供します。使用するのはおそらく少し面倒ですが、特にバインドパラメータの数を変えると、より柔軟になります。

使用方法は次のとおりです(警告:これはテストしていません):

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; 

(注:DBMS_SQL.TO_REFCURSOROracle 11gの新機能です。)

于 2012-06-12T20:52:43.940 に答える
9

これはそれほどエレガントではありませんが、3つのバインド変数の一部がヌルであっても、常にすべてを指定できることを意味します。必要な場合にのみ、追加のWHERE句を追加します。

(動的SQLを読みやすくするためにフォーマットしようとしましたが、1つの長い文字列として指定するだけで済みます)。

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;

私はDBアクセスのあるワークステーションの前にいないので、関数をテストすることはできませんが、近いはずです(構文エラーはご容赦ください、長い一日でした!)

それが役に立てば幸い...

于 2012-06-12T20:05:19.827 に答える
1

私が使用するアプローチは、動的SQLにELSEの場合、IFの逆の状態を含めることです。あなたのコードはpNameがnullでないことをテストしているので、生成されたクエリにpNameがNullであることをテストする句を追加します。そうすれば、クエリの結果に影響を与えることなく、毎回同じパラメータを渡すことができます。

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;
于 2014-05-23T10:13:13.700 に答える
0

どうですか

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);

于 2012-06-13T10:57:59.137 に答える