【问题标题】:Oracle SQL - How to build where clause with optional search parametersOracle SQL - 如何使用可选搜索参数构建 where 子句
【发布时间】:2015-05-26 09:39:57
【问题描述】:

页面中有四个字段

EMPLOYEE ID
DEPT
LOCATION
UNIT:

用户可以输入任何字段值都是可选的,如果他输入EMPLOYEE ID,那么查询应该返回与EMPLOYEE ID相关的行。如果他只输入LOCATION,那么查询应该返回该位置的所有员工。如何编写带可选参数的where子句条件。

【问题讨论】:

  • 和(:val = employeeid 或 :val 为空)使用此块。如果定义了值,则在查询中使用如果没有,则 null 为 null )
  • 我厌倦了你的选择,它没有返回我所期望的。你能告诉我所有四个字段如何构建 where 子句吗?

标签: oracle optional-parameters


【解决方案1】:

如果您在谓词中使用NVL,Oracle 可能会构建一个优化良好的查询:

select *
  from employee
 where employee_id = nvl(:employee_id, employee_id)
   and dept = nvl(:dept, dept)
   and location = nvl(:location, location)
   and unit = nvl(:unit, unit)

上面的代码大部分等同于 LeoLozes 的回答。尽管他的答案更具可读性,但在这种情况下,神秘版本可能会运行得更快。一个重要的区别是,如果列为 NULL,上述代码将不起作用。如果您有可为空的列,则需要使用类似 LeoLoze 的答案,因为 null = null 不正确。

Oracle 习惯了NVL 技巧,可以使用 FILTER 操作自动将此静态查询转换为动态查询。执行计划将有一个 FULL TABLE SCAN 和一个 INDEX RANGE SCAN,并且会在运行时根据绑定变量的值选择合适的一个。请参阅我的回答 here 以获取一些演示其工作原理的示例代码。

【讨论】:

  • 完全正确,我忘了这是最好的方法:)
  • 这不会选择至少有一个列值设置为 NULL 的条目。如果一个条目的 UNIT 为空,并且参数 :unit 也为空,那么条件 unit = nvl(:unit, unit) 将是 unit = nvl(null, null) 相当于 unit = null 始终为假(我们需要使用 UNIT IS NULL 代替)。
  • @AntoineMartin 感谢您的评论。我在答案中添加了关于可空列的警告。
【解决方案2】:

嗯,总是有(非常糟糕的优化)这样做的选择:

SELECT * 
FROM EMPLOYEE 
WHERE (EMPLOYEE_ID = :p_EMPLOYEE_ID OR :p_EMPLOYEE_ID IS Null)
  AND (DEPT = :p_DEPT OR :p_DEPT IS Null)
  AND (LOCATION = :p_LOCATION OR :p_LOCATION IS Null)
  AND (UNIT = :p_UNIT OR :p_UNIT IS Null)

我只在行数很少的表中使用它。 然而,建议至少有一个将使用索引字段的强制参数(因为这里,您将有一个 TABLE ACCESS FULL)。

【讨论】:

  • 不确定 Oracle 是否从左到右计算,但我总是这样写:(:p_EMPLOYEE_ID IS Null or EMPLOYEE_ID = :p_EMPLOYEE_ID)
  • @Rene 条件的顺序根本不重要。 Oracle 的问题是 SQL 的执行计划必须针对绑定参数值的任何组合进行优化。它必须在 Oracle 甚至看到绑定变量之前进行优化。对于搜索条件的 4 个可能列,仍然可以使用 2^4-1 个可能的 SQL 文本。
【解决方案3】:

我建议在现实世界的应用程序中反对这种想法。向数据库提交动态样式查询已证明在安全性、优化和功能正确性方面存在问题。 由于用户界面和数据库之间会有一些应用程序代码,因此最好根据需要在那里构建查询,然后提交执行。

【讨论】:

    【解决方案4】:
    select *
      from employee
     where nvl(employee_id, -1) = coalesce(:employee_id, employee_id, -1)
       and nvl(dept, -1) = coalesce(:dept, dept, -1)
       and nvl(location, -1) = coalesce(:location, location, -1)
       and nvl(unit, -1) = coalesce(:unit, unit, -1)
    

    这背后的原因是,Jon 建议的方式使用nvl 有一个缺点:如果被测试的字段本身是null,它将在该测试中返回false,因为(我相信)用于测试@987654325 @你需要用is null来测试它。

    nvl 可能在那里执行null = null 而不是null is null,返回错误的数据,您甚至不会注意到,因为它不会警告您。

    使用coalesce,当参数和字段均为null时,您将获得-1 = -1(或您认为对于该列不可能的任何其他值)

    【讨论】:

      【解决方案5】:

      为这么多编写代码的唯一安全方法是基于他们选择的单独视图,例如,如果您使用 Java GUI,您可以根据他们想要搜索的内容来选择信息。

      【讨论】:

      • 想详细说明一下?您是否建议 OP 为每种可能的搜索参数组合创建一个视图?这将很难扩展(2**4 = 16 个视图,仅 4 个搜索参数)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-25
      • 2016-03-06
      • 2012-04-28
      相关资源
      最近更新 更多