【问题标题】:Using Oracle collections in Dynamic SQL在动态 SQL 中使用 Oracle 集合
【发布时间】:2010-08-26 08:34:38
【问题描述】:

我需要检查过程参数是否为空,如果不是,则在 WHERE 子句中使用它。例如:

sqlquery := 'SELECT * FROM table WHERE a_col = a AND';
IF b IS NOT NULL THEN
  sqlquery := sqlquery || ' b_col = :b';
END IF;
IF c IS NOT NULL THEN
  sqlquery := sqlquery || ' c_col = :c';
END IF;

等等。

然后我需要使用 OPEN-FOR-USING 语句为已形成的 sqlquery 打开游标,但之前我应该​​决定通过 USING 子句发送哪些值。我看到的唯一方法是使用大量的 IF 子句:

IF b IS NOT NULL AND c IS NOT NULL THEN
  OPEN cur FOR sqlquery USING b, c;
ELSIF b IS NOT NULL THEN
  OPEN cur FOR sqlquery USING b;
ELSIF c IS NOT NULL THEN
  OPEN cur FOR sqlquery USING c;
ELSE 
  OPEN cur FOR sqlquery;

对于 N 个值,我得到大量的 IF 子句。如果没有很多 IF,我该如何解决这个问题?我认为可以使用 Oracle Collections,但我没有找到任何示例。

【问题讨论】:

    标签: oracle collections dynamic-sql


    【解决方案1】:

    您需要构建查询,使其始终引用 :b 和 :c 类似这样的内容:

    sqlquery := 'SELECT * FROM table WHERE a_col = a';
    IF b IS NOT NULL THEN
      sqlquery := sqlquery || ' AND b_col = :b';
    ELSE 
      sqlquery := sqlquery || ' AND :b IS NULL';
    END IF;
    IF c IS NOT NULL THEN
      sqlquery := sqlquery || ' AND c_col = :c';
    ELSE 
      sqlquery := sqlquery || ' AND :c IS NULL';
    END IF;
    

    那么你可以这样使用它:

    OPEN cur FOR sqlquery USING b, c;
    

    其实我觉得这样做可能效率更高:

    sqlquery := 'SELECT * FROM table WHERE a_col = a';
    IF b IS NOT NULL THEN
      sqlquery := sqlquery || ' AND b_col = :b';
    ELSE 
      sqlquery := sqlquery || ' AND (1=1 OR :b IS NULL)';
    END IF;
    IF c IS NOT NULL THEN
      sqlquery := sqlquery || ' AND c_col = :c';
    ELSE 
      sqlquery := sqlquery || ' AND (1=1 OR :c IS NULL)';
    END IF;
    

    ... 因为这样优化器可以识别 1=1 始终为真,因此可以完全忽略该谓词。我记得在某处读过(我认为是 Oracle Mag 中的 Tom Kyte 文章),但现在找不到参考。

    【讨论】:

    • 这个页面有一些讨论和文章链接我相信你指的是:asktom.oracle.com/pls/apex/…
    • 谢谢,这就是我一直在寻找的东西 =)
    • @Patrick:谢谢,这就是我的意思。 Oracle 似乎已经调整了他们所有的 URL,而我之前的 Delicious 链接已经失效了!
    【解决方案2】:

    您可以让 SQL 查询完成检查每个项目是否为 NULL 的工作。

    sqlquery := 'SELECT * FROM table WHERE a_col = a'||
                ' AND (:b IS NULL OR b_col = :b)'||
                ' AND (:c IS NULL OR b_col = :c)';
    
    OPEN cur FOR sqlquery USING b,b,c,c;
    

    您确实必须重复 USING 子句中的变量,这很遗憾地为拼写错误创造了机会,从而导致令人困惑的错误。但至少你只需要做对一次。

    【讨论】:

    • 这失去了动态查询的优势;它可能总是会导致全表扫描。
    【解决方案3】:

    你试过了吗?

    select * from my_table
    where a_col = :a
      and b_col = (case when :b is not null then :b else b_col end)
      and c_col = (case when :c is not null then :c else c_col end)
      and d_col = (case when :d is not null then :d else d_col end)
    ;
    

    【讨论】:

      【解决方案4】:

      我也遇到过类似情况。

      我制定了可行的执行计划,并为它们使用了单独的查询。

      例如,假设我正在搜索人员并且参数是 first_name、last_name。性别,出生日期。该表在 (last_name,first_name) 和 (date_of_birth) 上有索引。

      IF :p_firstname IS NOT NULL and :p_lastname IS NOT NULL THEN
        OPEN cur FOR 
          'SELECT * FROM PEOPLE WHERE last_name=:a AND first_name=:b AND
           (date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
      ELSIF :p_lastname IS NOT NULL THEN
        OPEN cur FOR 
          'SELECT * FROM PEOPLE WHERE last_name=:a AND 
           (date_of_birth = :c or :c is NULL) AND (gender = :d or :d IS NULL)' USING ....
      ELSIF :p_dateofbirth IS NOT NULL THEN
        OPEN cur FOR 
          'SELECT * FROM PEOPLE WHERE date_of_birth=:a AND 
           (first_name=:b OR :b IS NULL) AND (gender = :d or :d IS NULL)' USING ....
      ELSE
        RAISE_APPLICATION_ERROR(-20001,'Last Name or Date of Birth MUST be supplied);
      END IF;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-26
        • 2014-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多