【问题标题】:Binding Parameters to Oracle Dynamic SQL将参数绑定到 Oracle 动态 SQL
【发布时间】:2012-06-15 18:04:06
【问题描述】:

我有一个接受多个参数(即 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;

如果我执行传递所有参数的过程,它会正常运行。

但是如果我只传递了一个或两个参数,那么程序就会出错:

ORA-01006: bind variable does not exist

如何根据参数值的使用位置有选择地将变量与参数绑定?例如,如果只传递了 pName,那么我将只执行查询:

OPEN TestCursor FOR SQLQuery USING pName;

或者如果 pName 和 pTeam 都通过了,那么:

OPEN TestCursor FOR SQLQuery USING pName, pTeam;

希望有人可以提供更多解决此问题的方法。谢谢。

编辑: 我实际上可以使用以下内容:

-- 根据传递的参数构建查询。 如果 pName 不为空 SQLQuery := SQLQuery || '和名字一样''' ||姓名 || ''''; 如果结束;

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 注入的影响...

【问题讨论】:

    标签: oracle parameters dynamic-sql


    【解决方案1】:

    我使用的方法是在动态 SQL 中包含一个 ELSE 案例,该案例的状态与 IF 的相反。您的代码测试 pName 不为空,因此我将在生成的查询中添加一个子句来测试 pName IS 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;
    

    【讨论】:

      【解决方案2】:

      怎么样

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

      ?

      【讨论】:

      • like '%' 不会影响 null。所以如果其中一些字段为空,你会得到错误
      【解决方案3】:

      这不是非常优雅,但它意味着您可以始终提供所有三个绑定变量,即使其中一些为空。如果需要,您只需添加额外的 WHERE 子句。

      (我已尝试格式化动态 SQL 以使其更具可读性,您可以将其作为一个长字符串提供)。

      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_binds 是在哪里声明的?还是我需要在某处声明?
      • 它在SQL语句的WITH子句中声明,它是一个存放绑定变量的虚拟表。见这里:orafaq.com/node/1879
      • 试过了,但还是说 ORA-01008: not all variables bound
      • 如果您需要帮助解决问题,您必须提供比“尝试过但仍然显示 ORA-01008:并非所有变量都绑定”更多的信息。我自己在几乎完全相同的情况下使用这种方法,它工作正常,语法可能有点偏离我的答案,但基本前提是合理的。
      • 我基本上执行了你提供的过程......它说变量没有绑定。
      【解决方案4】:

      您可以使用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_REFCURSOR 是 Oracle 11g 中的新增内容。)

      【讨论】:

      • +1,我喜欢使用 DBMS_SQL,因为它更“受支持”,但比我建议的方法涉及更多。
      猜你喜欢
      • 1970-01-01
      • 2019-08-24
      • 2016-12-15
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多