Оценка функций Oracle и SQLServer в запросах - PullRequest
5 голосов
/ 17 июня 2009

Допустим, у меня есть вызов функции для select или , где предложение в Oracle, например:

select a, b, c, dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3)
  from my_table

Аналогичный пример может быть создан для MS SQLServer.

Какое поведение ожидается в каждом случае?

Будет ли функция HASH вызываться один раз для каждой строки в таблице, или СУБД будет достаточно умна, чтобы вызывать функцию только один раз, поскольку это функция с постоянными параметрами и без побочные эффекты

Большое спасибо.

Ответы [ 3 ]

8 голосов
/ 17 июня 2009

Ответ для Oracle - это зависит. Функция будет вызываться для каждой выбранной строки, ЕСЛИ функция не будет помечена как «детерминированная», и в этом случае она будет вызываться только один раз.

CREATE OR REPLACE PACKAGE TestCallCount AS
    FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER;
    FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC;
    FUNCTION GetCallCount RETURN INTEGER;
    FUNCTION GetCallCount2 RETURN INTEGER;
END TestCallCount;

CREATE OR REPLACE PACKAGE BODY TestCallCount AS
    TotalFunctionCalls INTEGER := 0;
    TotalFunctionCalls2 INTEGER := 0;

    FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER AS
    BEGIN
        TotalFunctionCalls := TotalFunctionCalls + 1;
        RETURN Length(SrcStr);
    END;
    FUNCTION GetCallCount RETURN INTEGER AS
    BEGIN
        RETURN TotalFunctionCalls;
    END;

    FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC AS
    BEGIN
        TotalFunctionCalls2 := TotalFunctionCalls2 + 1;
        RETURN Length(SrcStr);
    END;
    FUNCTION GetCallCount2 RETURN INTEGER AS
    BEGIN
        RETURN TotalFunctionCalls2;
    END;

END TestCallCount;




SELECT a,TestCallCount.StringLen('foo') FROM(
    SELECT 0 as a FROM dual
    UNION
    SELECT 1 as a FROM dual
    UNION
    SELECT 2 as a FROM dual
);

SELECT TestCallCount.GetCallCount() AS TotalFunctionCalls FROM dual;

Выход:

A                      TESTCALLCOUNT.STRINGLEN('FOO') 
---------------------- ------------------------------ 
0                      3                              
1                      3                              
2                      3                              

3 rows selected


TOTALFUNCTIONCALLS     
---------------------- 
3                      

1 rows selected

Таким образом, функция StringLen () была вызвана три раза в первом случае. Теперь при выполнении с StringLen2 (), который обозначается как детерминированный:

SELECT a,TestCallCount.StringLen2('foo') from(
    select 0 as a from dual
    union
    select 1 as a from dual
    union
    select 2 as a from dual
);

SELECT TestCallCount.GetCallCount2() AS TotalFunctionCalls FROM dual;

Результаты:

A                      TESTCALLCOUNT.STRINGLEN2('FOO') 
---------------------- ------------------------------- 
0                      3                               
1                      3                               
2                      3                               

3 rows selected

TOTALFUNCTIONCALLS     
---------------------- 
1                      

1 rows selected

Таким образом, функция StringLen2 () была вызвана только один раз, поскольку она была помечена как детерминированная.

Для функции, не помеченной как детерминированная, вы можете обойти это, изменив свой запрос следующим образом:

select a, b, c, hashed
  from my_table
cross join (
  select dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3) as hashed from dual
);
4 голосов
/ 17 июня 2009

Для сервера SQL он будет оцениваться для каждой строки.

Вы будете НАМНОГО лучше, запустив функцию один раз и присвоив ей переменную и используя переменную в запросе.

1 голос
/ 17 июня 2009

короткий ответ .... это зависит.

Если функция обращается к данным, ORACLE не знает, будет ли она одинаковой для каждой строки, поэтому ей необходимо выполнить запрос для каждой из них. Если, например, ваша функция является просто средством форматирования, которое всегда возвращает одно и то же значение, вы можете включить кэширование (пометив его как детерминированный), что может позволить вам выполнить вызов функции только один раз.

Что-то, на что вы можете посмотреть, это ORACLE WITH подзапрос:

Предложение WITH query_name позволяет вам назначить имя блоку подзапроса. Вы затем может ссылаться на блок подзапроса несколько мест в запросе по указав имя запроса. оракул оптимизирует запрос, обрабатывая имя запроса в виде встроенного представления или как временная таблица

Я получил цитируемый текст от здесь , в котором есть множество примеров.

...