LNNVL 替换 NOT IN?

LNNVL to replace NOT IN?

这个有点难搜索,所以我在这里问(我在那里看到了关于 lnnvl() 的问题)。

我的查询如下所示:

SELECT *
FROM foo
WHERE foo.bar NOT IN ('X', 'Y')
   OR foo.bar IS NULL;

不久前我了解了 lnnvl() 并且我能够用以下方法做同样的事情:

SELECT *
FROM foo
WHERE lnnvl(foo.bar = 'X')
  AND lnnvl(foo.bar = 'Y');

太棒了,但在检查更大的集合时可扩展性不是很好。有没有你们知道的更清洁的方法?过去,我做过类似的事情:

SELECT *
FROM foo
WHERE nvl(foo.bar, ' ') NOT IN ('X', 'Y')

感谢任何见解!

你的最后一个选项很好,并导致以下 filterpredicate

filter(NVL("FOO"."BAR",' ')<>'X' AND NVL("FOO"."BAR",' ')<>'Y')

我看不到其他可能性,因为下面的谓词会导致异常

WHERE lnnvl(foo.bar IN ('X', 'Y')); 
ORA-13207: incorrect use of the [LNNVL] operator

如果您的 table 非常 大并且您想避免 完整 table 扫描 您甚至可以将 基于函数的索引 定义为

create index idx on foo(nvl(bar,' ')); 

这将防止完全table扫描,但不幸的是索引不能像往常一样使用-因为你检查 不公平性 <>.

所以结果是全索引扫描,即你遍历整个索引并过滤不匹配的keys, 访问table 只为正确的keys.

如果 table 很大而结果集很小,这可能是有意义的。

执行计划

--------------------------------------------------------------------------------------------
| Id  | Operation                           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |      |   250 |   489K|   269   (1)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| FOO  |   250 |   489K|   269   (1)| 00:00:01 |
|*  2 |   INDEX FULL SCAN                   | IDX  |   250 |       |   185   (2)| 00:00:01 |
--------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter(NVL("BAR",' ')<>'X' AND NVL("BAR",' ')<>'Y')

顺便说一句

您不能对谓词 lnnvl(bar = 'X') 使用索引,这显然会导致 full table scan 与谓词 filter(LNNVL("BAR"='X'))

无论如何,您可以将谓词重新表述为

WHERE 
case when lnnvl(bar = 'X') then 1 end = 1

并定义FBI索引

create index idx2 on foo( case when lnnvl(bar = 'X') then 1 end ); 

这导致 不错 index range scan

您甚至可以使用 and 例如

where case when lnnvl(bar = 'X') and lnnvl(bar = 'Y') then 1 end = 1