Есть ли лучший способ поиска по диапазону значений в Oracle, чем тестирование по подзапросу?
Вопрос
учитывая эту таблицу:
x y
-- -
10 a
20 b
30 c
Мне нужен наилучший способ сопоставления значений
[10,20) -> a
[20,30) -> b
[30,inf) -> c
Прямо сейчас я использую запрос типа:
select y from foo
where x=(select max(x) from foo
where x<=21);
Есть ли лучший способ сделать это?Существует ли аналитическая функция это могло бы помочь?
Вот мой тестовый пример:
create table foo as
select 10 as x ,'a' as y from dual union
select 20,'b' from dual union
select 30,'c' from dual;
-- returns: a,b,b:
select y from foo where x=(select max(x) from foo where x<=19);
select y from foo where x=(select max(x) from foo where x<=20);
select y from foo where x=(select max(x) from foo where x<=21);
Решение
Вы можете переписать свой запрос, чтобы обращаться к таблице foo только один раз, а не дважды, используя агрегатную функцию MAX-KEEP.
Пример:
SQL> var N number
SQL> exec :N := 19
PL/SQL-procedure is geslaagd.
SQL> select max(y) keep (dense_rank last order by x) y
2 from foo
3 where x <= :N
4 /
Y
-
a
1 rij is geselecteerd.
SQL> exec :N := 20
PL/SQL-procedure is geslaagd.
SQL> select max(y) keep (dense_rank last order by x) y
2 from foo
3 where x <= :N
4 /
Y
-
b
1 rij is geselecteerd.
SQL> exec :N := 21
PL/SQL-procedure is geslaagd.
SQL> select max(y) keep (dense_rank last order by x) y
2 from foo
3 where x <= :N
4 /
Y
-
b
1 rij is geselecteerd.
Также a, b, b в результате.Планы запросов:
SQL> set serveroutput off
SQL> select /*+ gather_plan_statistics */
2 y
3 from foo
4 where x = (select max(x) from foo where x<=:N)
5 /
Y
-
b
1 rij is geselecteerd.
SQL> select * from table(dbms_xplan.display_cursor(null,null,'predicate -note last'))
2 /
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_ID 3kh85qqnb2phy, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ y from foo where x =
(select max(x) from foo where x<=:N)
Plan hash value: 763646971
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 8 (100)| |
|* 1 | TABLE ACCESS FULL | FOO | 1 | 16 | 4 (0)| 00:00:01 |
| 2 | SORT AGGREGATE | | 1 | 13 | | |
|* 3 | TABLE ACCESS FULL| FOO | 2 | 26 | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("X"=)
3 - filter("X"<=:N)
22 rijen zijn geselecteerd.
SQL> select max(y) keep (dense_rank last order by x) y
2 from foo
3 where x <= :N
4 /
Y
-
b
1 rij is geselecteerd.
SQL> select * from table(dbms_xplan.display_cursor(null,null,'predicate -note last'))
2 /
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_ID avm2zh62c8cwd, child number 0
-------------------------------------
select max(y) keep (dense_rank last order by x) y from foo where x
<= :N
Plan hash value: 3274996510
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
| 1 | SORT AGGREGATE | | 1 | 16 | | |
|* 2 | TABLE ACCESS FULL| FOO | 1 | 16 | 4 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("X"<=:N)
20 rijen zijn geselecteerd.
Два полных сканирования таблицы в foo против одного для нового запроса.
С уважением, Роб.
Другие советы
select distinct first_value(y) over (order by x desc) from foo where x<=19;
select distinct first_value(y) over (order by x desc) from foo where x<=20;
select distinct first_value(y) over (order by x desc) from foo where x<=21;
Plus:индекс на x, вероятно, будет хорошей идеей.
Вот еще один ответ, полученный через usenet.Пока что этот вариант, по-видимому, имеет наиболее эффективное исполнение.
select max(y) keep (dense_rank last order by x) from foo where x<=21;
Не связан с StackOverflow