Oracle 可选绑定变量

Posted

技术标签:

【中文标题】Oracle 可选绑定变量【英文标题】:Oracle optional bind variables 【发布时间】:2017-10-09 14:55:28 【问题描述】:

在给定用户 ID 的情况下,我有一个查询要从表中选择用户。该参数是可选的。

这是查询:

SELECT * FROM USERS
WHERE (USER_ID = :USER_ID OR :USER_ID IS NULL)
ORDER BY USER_ID;

现在我执行查询找到一个用户,所以:USER_ID 获得了 valor 1:

SELECT * FROM USERS
WHERE (USER_ID = 1 OR 1 IS NULL)
ORDER BY USER_ID;

这个查询需要 5 秒。

然后,我多次添加到前面的查询OR :USER_ID IS NULL。此示例比第一个示例花费更多时间:

SELECT * FROM USERS
WHERE (USER_ID = 1 OR 1 IS NULL [OR 1 IS NULL]x100)
ORDER BY USER_ID;

此查询需要 30 秒。


两个例子的执行计划是一样的:

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |  3256K|   695M|       |   682K  (1)| 00:00:27 |       |       |
|   1 |  SORT ORDER BY              |         |  3256K|   695M|   877M|   682K  (1)| 00:00:27 |       |       |
|   2 |   PARTITION RANGE ALL       |         |  3256K|   695M|       |   534K  (1)| 00:00:21 |     1 |1048575|
|*  3 |    TABLE ACCESS STORAGE FULL| USERS |  3256K|   695M|       |   534K  (1)| 00:00:21 |     1 |1048575|

Oracle 版本: Oracle Database 12c


为什么 oracle 不接受第一个声明,它总是正确的,并停止评估其余部分?

【问题讨论】:

桌子上有索引吗? 您运行了多少次查询? Oracle 必须对修改后的查询进行硬解析,并且第一次这样做会增加时间 - 第二次和随后的时间它可以执行软解析并可能从其缓存中检索结果。要比较查询,您可能需要刷新结果缓存或多次运行两者。 我对每个查询运行了几次。而且总是第二个需要更多的时间。 调查/发布两个查询的执行计划,如here 所述。您会立即看到它们之间的区别,并且可能还有USER_ID上的缺失索引 可能duplicate 【参考方案1】:

您的问题是OR谓词触发的大表上的FULL TABLE SCAN

根据绑定变量的值,查询返回一行(如果绑定变量不为 NULL),否则返回整个表。

对于一个绑定变量,您可以使用NVL 技巧

SELECT * FROM USERS
WHERE (USER_ID = nvl(:USER_ID, USER_ID))
ORDER BY USER_ID;

这导致了一个包含两个部分的执行计划,涵盖了这两种情况:

BV 为 NULL -> 全面扫描

BV 不为空 -> 索引访问

--------------------------------------------------------------------------------------------
| Id  | Operation                      | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |           |  8329 |  9313K|   941   (1)| 00:00:12 |
|   1 |  SORT ORDER BY                 |           |  8329 |  9313K|   941   (1)| 00:00:12 |
|   2 |   CONCATENATION                |           |       |       |            |          |
|*  3 |    FILTER                      |           |       |       |            |          |
|*  4 |     TABLE ACCESS FULL          | USERS     |  8247 |  9221K|   925   (1)| 00:00:12 |
|*  5 |    FILTER                      |           |       |       |            |          |
|   6 |     TABLE ACCESS BY INDEX ROWID| USERS     |    82 | 93890 |    15   (0)| 00:00:01 |
|*  7 |      INDEX RANGE SCAN          | USERS_IDX |  1110 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter(:USER_ID IS NULL)
   4 - filter("USER_ID" IS NOT NULL)
   5 - filter(:USER_ID IS NOT NULL)
   7 - access("USER_ID"=:USER_ID)

因此,如果传递了 BV(非 NULL)AND 上的 USER_ID 上的索引已定义,这将快速响应。 这将导致整个表的FULL TABLE SCAN(5 秒)AND SORT(我猜还有 25 秒),总共 30 秒响应。

请注意,如果您通过 BV,您只执行FULL TABLE SCANSORT 时间可以忽略不计,因为只返回一条记录(假设 USER_ID 为 PK) - 这解释了响应时间的差异。

【讨论】:

以上是关于Oracle 可选绑定变量的主要内容,如果未能解决你的问题,请参考以下文章

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

为啥使用可选绑定?

oracle之绑定变量

mybatis最大绑定多少个变量

在 Oracle 中正确使用带日期的绑定变量?

Oracle之绑定变量 2