Oracle 可选绑定变量
Oracle optional bind variables
我有一个查询来自 table 的 select 用户,给定用户 ID。此参数是可选的。
这是查询:
SELECT * FROM USERS
WHERE (USER_ID = :USER_ID OR :USER_ID IS NULL)
ORDER BY USER_ID;
现在我执行查找一个用户的查询,所以 :USER_ID
取值 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 不采用第一个语句,它始终为真,并停止评估其余部分?
您的问题是由 OR
谓词触发的大型 table 上的 FULL TABLE SCAN
。
根据绑定变量的值查询 returns 一行(如果绑定变量不为 NULL)或整个 table 否则。
对于只有一个绑定变量,您可以使用 NVL
技巧
SELECT * FROM USERS
WHERE (USER_ID = nvl(:USER_ID, USER_ID))
ORDER BY USER_ID;
这导致执行计划由两部分组成,涵盖两种情况:
BV 为空 -> 全扫描
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
上的索引,这将快速响应。
这将导致整个 table 的 FULL TABLE SCAN
(5 秒)AND SORT
(我猜还有 25 秒),总共 30秒响应。
请注意,如果您通过 BV,则仅执行 FULL TABLE SCAN
,SORT
时间可忽略不计,因为仅返回一条记录(假设 USER_ID 是 PK)-解释响应时间的差异。
我有一个查询来自 table 的 select 用户,给定用户 ID。此参数是可选的。
这是查询:
SELECT * FROM USERS
WHERE (USER_ID = :USER_ID OR :USER_ID IS NULL)
ORDER BY USER_ID;
现在我执行查找一个用户的查询,所以 :USER_ID
取值 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 不采用第一个语句,它始终为真,并停止评估其余部分?
您的问题是由 OR
谓词触发的大型 table 上的 FULL TABLE SCAN
。
根据绑定变量的值查询 returns 一行(如果绑定变量不为 NULL)或整个 table 否则。
对于只有一个绑定变量,您可以使用 NVL
技巧
SELECT * FROM USERS
WHERE (USER_ID = nvl(:USER_ID, USER_ID))
ORDER BY USER_ID;
这导致执行计划由两部分组成,涵盖两种情况:
BV 为空 -> 全扫描
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
上的索引,这将快速响应。
这将导致整个 table 的 FULL TABLE SCAN
(5 秒)AND SORT
(我猜还有 25 秒),总共 30秒响应。
请注意,如果您通过 BV,则仅执行 FULL TABLE SCAN
,SORT
时间可忽略不计,因为仅返回一条记录(假设 USER_ID 是 PK)-解释响应时间的差异。