Oracle - 如何使用动态绑定参数定义动态 SQL?

Posted

技术标签:

【中文标题】Oracle - 如何使用动态绑定参数定义动态 SQL?【英文标题】:Oracle - How to define Dynamic SQL with dynamic bind parameters? 【发布时间】:2018-03-02 21:56:29 【问题描述】:

我在 Oracle 数据库中有这个简单的搜索存储过程,并希望动态地为我的绑定变量指定值。例如,如果只指定了员工编号,我的 sql 将只有一个绑定变量,但如果同时指定员工编号和姓名,我的 sql 将有两个绑定变量。如何在“USING”子句中实现这一点?

CREATE OR REPLACE PROCEDURE SP_EMPLOYEE_SEARCH_TEST 
    (IN_EMP_NO              IN VARCHAR2,
    IN_EMP_NAME              IN VARCHAR2,
    OUT_C_SEARCH_RESULT           OUT sys_refcursor)  
IS
SQL_QUERY VARCHAR2(500);

BEGIN

SQL_QUERY := 'SELECT * FROM EMPLOYEE WHERE ' ;

IF (IN_EMP_NO IS NOT NULL) THEN
    SQL_QUERY :=  SQL_QUERY || 'EMPLOYEE_NO = :1 ';
END IF;

IF (IN_EMP_NAME IS NOT NULL) THEN
    SQL_QUERY :=  SQL_QUERY || ' AND EMPLOYEE_NAME = :2';
END IF;

dbms_output.put_line(SQL_QUERY);

OPEN OUT_C_SEARCH_RESULT FOR
    SQL_QUERY USING <<How to define dynamically>>;   

END SP_EMPLOYEE_SEARCH_TEST;

【问题讨论】:

Vivek,类似于你对我的提议,如果搜索仅基于employee_name,则IF 语句存在问题。那么你应该在一开始就去掉 AND。 最佳方法是针对不同的可能性使用不同的 SQL 语句,然后只运行“适合”的语句。否则,如果您编写一个复杂的WHERE 子句来适应各种组合,至少在某些情况下,生成的查询会很慢。 @mathguy 这是一个巨大的搜索页面,可能有相当大的可能性,因此正在寻找一些正确的最佳解决方案。 【参考方案1】:

如果你有最大值。 2-3个可选参数我会选择手动方式:

IF IN_EMP_NO IS NULL AND IN_EMP_NAME IS NULL THEN
    OPEN OUT_C_SEARCH_RESULT FOR SQL_QUERY;
ELSIF IN_EMP_NO IS NOT NULL AND IN_EMP_NAME IS NULL THEN
    OPEN OUT_C_SEARCH_RESULT FOR SQL_QUERY USING IN_EMP_NO;
ELSIF IN_EMP_NO IS NULL AND IN_EMP_NAME IS NOT NULL THEN
    OPEN OUT_C_SEARCH_RESULT FOR SQL_QUERY USING IN_EMP_NAME;
ELSIF IN_EMP_NO IS NOT NULL AND IN_EMP_NAME IS NOT NULL THEN
    OPEN OUT_C_SEARCH_RESULT FOR SQL_QUERY USING IN_EMP_NO, IN_EMP_NAME;
END IF;

或像这样构建您的查询:

SQL_QUERY := 'SELECT * FROM EMPLOYEE WHERE ' ;
SQL_QUERY :=  SQL_QUERY || ' (EMPLOYEE_NO = :1 OR :2 IS NULL)';
SQL_QUERY :=  SQL_QUERY || ' AND (EMPLOYEE_NAME = :3 OR :4 IS NULL)';
OPEN OUT_C_SEARCH_RESULT FOR
   SQL_QUERY USING IN_EMP_NO, IN_EMP_NO, IN_EMP_NAME, IN_EMP_NAME;  

否则,如果您想获得更大的灵活性,请查看DBMS_SQL 包。一个有效的例子是这个:

CREATE OR REPLACE PROCEDURE SP_EMPLOYEE_SEARCH_TEST 
    (IN_EMP_NO              IN VARCHAR2,
    IN_EMP_NAME              IN VARCHAR2,
    OUT_C_SEARCH_RESULT           OUT sys_refcursor)  
IS

SQL_QUERY VARCHAR2(500);
cur INTEGER;
ret NUMBER;
bind BOOLEAN := FALSE;

BEGIN


IF (IN_EMP_NO IS NOT NULL) THEN
    SQL_QUERY :=  SQL_QUERY || 'AND EMPLOYEE_NO = :p1 '; -- note the space at the end!
    bind := TRUE;
END IF;

IF (IN_EMP_NAME IS NOT NULL) THEN
    SQL_QUERY :=  SQL_QUERY || 'AND EMPLOYEE_NAME = :p2 '; 
    bind := TRUE;
END IF;

IF bind then
   SQL_QUERY := 'SELECT * FROM EMPLOYEE '||REGEXP_REPLACE(SQL_QUERY, '^AND', 'WHERE');    
ELSE
   SQL_QUERY := 'SELECT * FROM EMPLOYEE';
END IF:
dbms_output.put_line(SQL_QUERY);

cur := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cur, SQL_QUERY, DBMS_SQL.NATIVE);

IF (IN_EMP_NO IS NOT NULL) THEN
    DBMS_SQL.BIND_VARIABLE(cur, ':p1', IN_EMP_NO);
END IF;    
IF (IN_EMP_NAME IS NOT NULL) THEN
    DBMS_SQL.BIND_VARIABLE(cur, ':p2', IN_EMP_NAME);
END IF;

ret := DBMS_SQL.EXECUTE(cur);
OUT_C_SEARCH_RESULT := DBMS_SQL.TO_REFCURSOR(cur);


END SP_EMPLOYEE_SEARCH_TEST;

【讨论】:

我相信你的第二种方法对我有用。我唯一担心的是它会添加很多额外的绑定变量。如果有机会,请提供具体示例。 @VivekN,请参阅使用 DBMS_SQL 包的示例。 我喜欢 DBMS_SQL 包解决方案,但我选择使用本机动态 sql 选项,因为它符合我的需要并且速度更快。【参考方案2】:

只需同时传递它们,但更改您的动态部分:

IF (IN_EMP_NO IS NOT NULL) THEN
    SQL_QUERY :=  SQL_QUERY || 'EMPLOYEE_NO = :1 ';
ELSE
    SQL_QUERY :=  SQL_QUERY || ':1 IS NULL ';
END IF;

IF (IN_EMP_NAME IS NOT NULL) THEN
    SQL_QUERY :=  SQL_QUERY || ' AND EMPLOYEE_NAME = :2';
ELSE
    SQL_QUERY :=  SQL_QUERY || ' AND :2 IS NULL';
END IF;

这样查询仍然有相同数量的绑定。

你也可以这样重写查询:

SELECT * FROM EMPLOYEE
 WHERE EMPLOYEE_NO = NVL(:1, EMPLOYEE_NO)
   AND EMPLOYEE_NAME = NVL(:2, EMPLOYEE_NAME)

当绑定值为空时,它仍然采用相同数量的参数并获得所需的结果。

【讨论】:

employee_name = nvl(:2, employee_name) 将错过员工姓名为 NULL 的几行(如果有的话)。 如果其中任何一列包含空值,那么您是绝对正确的。冒着失去使用索引能力的风险,谓词可以这样写nvl(EMPLOYEE_NAME,'IS NULL') = coalesce(:1, EMPLOYEE_NAME, 'IS NULL') @Sentinel 我喜欢你的第一种方法。它允许我拥有相同数量的绑定变量。我会试试然后回来。 @Sentinel 我最终使用了您的第一种方法。它按预期工作。

以上是关于Oracle - 如何使用动态绑定参数定义动态 SQL?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle数据库中字段定义为Char类型,Hibernate用该字段进行动态绑定参数查询,获取不到结果的问题

Oracle数据库中字段定义为Char类型,Hibernate用该字段进行动态绑定参数查询,获取不到结果的问题

编程开发之--Oracle数据库--存储过程使用动态参数绑定

动态存储过程调用oracle中的动态参数绑定

在 Oracle 11g 中转换动态查询以使用绑定变量

如何为 SqlDataSource 动态绑定变量参数