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')
感谢任何见解!
你的最后一个选项很好,并导致以下 filter
predicate
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
这个有点难搜索,所以我在这里问(我在那里看到了关于 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')
感谢任何见解!
你的最后一个选项很好,并导致以下 filter
predicate
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