2014-02-20 2 views
7

Учитывая следующую таблицуПочему детерминированная функция выполняет дополнительное время в SQL?

create table tmp_test as 
select mod(level, 5) as n 
    from dual 
connect by level <= 10 
     ; 

и эту функцию

create or replace function test_deterministic (Pn in number 
     ) return number deterministic is 
begin 
    dbms_output.put_line(Pn); 
    dbms_lock.sleep(1); 
    return Pn; 
end; 

Он выполняет в 6 раз, с 6 секунд:

SQL> select test_deterministic(n) from tmp_test; 

TEST_DETERMINISTIC(N) 
--------------------- 
        1 
        2 
        3 
        4 
        0 
        1 
        2 
        3 
        4 
        0 

10 rows selected. 

1 
2 
3 
4 
0 
1 
Elapsed: 00:00:06.02 

я ожидал бы, чтобы это выполнить 5 раз. Если я запустил этот оператор SELECT в SQL Developer или PL/SQL Developer, он выполнит только 5 раз. Точно так же, если я запускаю это в PL/SQL он выполняет 5 раз:

SQL> begin 
    2  for i in (select test_deterministic(n) from tmp_test) loop 
    3  null; 
    4  end loop; 
    5 end; 
    6/
1 
2 
3 
4 
0 
Elapsed: 00:00:05.01 

Почему эта функция выполняется 6 раз при вызове в SQL из SQL * Plus? Я ожидал, что он будет выполняться 5 раз.

Я нахожусь в версии 11.2.0.3.5, а клиент SQL * Plus - это релиз 11.2.0.1.0 (64 бит).

+0

Вы не указали какие-либо типы данных в любом месте. Типы данных между двумя средами разные, даже с одинаковыми именами. Попробуйте определить тип данных в определении таблицы, а также сохраните свой уровень там, чтобы получить полную картину. –

+0

Это не имеет значения @ElectricLlama. После создания таблицы столбец N имеет тип данных NUMBER. Затем функция выполняется на столе и поэтому не связана с ней; это как-то связано с детерминистскими (я думаю?). – Ben

+0

Интересно. Я вижу то же самое в 10g (10.2.0.5), FWIW. –

ответ

7

Blame SQL * Plus, Ben. Ваша функция работает в этой ситуации правильно. Дополнительное значение (1) вы видите там из-за arraysize значения, и, в основном из-за того, как SQL * Plus извлекает строки. Сначала он извлекает первую строку и только затем начинает использовать arraysize для последующих выборок. Каждая новая выборка - это новый вызов базы данных, который заставляет определять вашу детерминированную функцию. Попробуйте установить arraysize на 1 или 2 (тот же эффект) и выполните инструкцию select. Первая строка возвращается, а затем, arraysize приходит играть и каждый последующий запрос будет возвращать несколько строк:

Arraysize установлен в 1 (два на самом деле)

SQL> set arraysize 1; 
SQL> select test_deterministic(n) from tmp_test; 

TEST_DETERMINISTIC(N)               
---------------------               
        1               
        2               
        3               
        4               
        0               
        1               
        2               
        3               
        4               
        0               

10 rows selected. 

1                    
2                    
3                    
4                    
0                    
1                    
2                    
3                    
4                    
0                    
Elapsed: 00:00:10.10 

же запрос с гораздо большими arraysize :

SQL> set arraysize 50; 
SQL> select test_deterministic(n) from tmp_test; 

TEST_DETERMINISTIC(N)               
---------------------               
        1               
        2               
        3               
        4               
        0               
        1               
        2               
        3               
        4               
        0               

10 rows selected. 

1                    
2                    
3                    
4                    
0                    
1                    
Elapsed: 00:00:06.06 

SQL> spool off; 

Любой другой клиент, будь то разработчик SQL или разработчик PL/SQL, не имеет такого поведения и дает правильный результат.

+0

Ух, это ужасно. Я предполагаю, что он работает в контексте PL/SQL в SQL * Plus, потому что SQL выполняется через механизм PL/SQL, а не SQL тогда? – Ben

+0

+1 для последней строки .. Я проверял то же самое в TOAD – SriniV

+0

Причина, по которой SQL Developer «не имеет такого поведения» заключается в том, что вы не можете управлять массивом там. В JDBC вы можете определить «fetchsize», который эквивалентен массиву в SQL * Plus. Если вы используете клиент SQL, в котором вы можете контролировать размер выборки, вы можете воспроизвести поведение SQL * Plus в клиенте на базе JDBC. –

Смежные вопросы