вызов функции оракула кэшируется во время массовой вставки - PullRequest
2 голосов
/ 02 ноября 2011

при вставке групповых строк и использовании вызова функции в качестве одного из значений столбца, я получаю точно такие же значения для каждых 10-11 строк из функции.функция фактически генерирует значения UUID и возвращает уникальные результаты.Если я заменю вызов функции в операторе вставки фактическим кодом функции, он никогда не повторяется.

Итак, из этого я заключаю, что оракул фактически кэширует результат функции и вызывает его только один раз для каждого10-11 строк вставляю.как я могу изменить это поведение?

функция, которую я вызываю, я взяла с http://www.oracle -base.com / article / 9i / UUID9i.php :

create or replace
FUNCTION        new_uuid RETURN VARCHAR2 AS
  l_seed        BINARY_INTEGER;
  l_random_num  NUMBER(5);
  l_date        VARCHAR2(25);
  l_random      VARCHAR2(4);
  l_ip_address  VARCHAR2(12);
BEGIN
  l_seed := TO_NUMBER(TO_CHAR(SYSDATE,'YYYYDDMMSS'));
  DBMS_RANDOM.initialize (val => l_seed);
  l_random_num := TRUNC(DBMS_RANDOM.value(low => 1, high => 65535));
  DBMS_RANDOM.terminate;

  l_date       := conversion_api.to_hex(TO_NUMBER(TO_CHAR(SYSTIMESTAMP,'FFSSMIHH24DDMMYYYY')));
  l_random     := RPAD(conversion_api.to_hex(l_random_num), 4, '0');
  l_ip_address := conversion_api.to_hex(TO_NUMBER(REPLACE(NVL(SYS_CONTEXT('USERENV','IP_ADDRESS'), '123.123.123.123'), '.', '')));

  RETURN SUBSTR(l_date, 1, 8)                     || '-' ||
         SUBSTR(l_date, 9, 4)                     || '-' ||
         SUBSTR(l_date, 13, 4)                    || '-' ||
         RPAD(SUBSTR(l_date, 17), 4, '0')         || '-' ||
         RPAD(L_RANDOM || L_IP_ADDRESS, 12, '0');
END;

и вот оператор вставки, который я использую:

INSERT INTO My_TABLE(ID, NAME,)
SELECT NEW_UUID(), NAME
FROM MY_TABLE2;
COMMIT;

выборка внутри этого оператора производит много повторяющихся UUID.в то время как это утверждение производит уникальные:

SELECT RPAD(RPAD(my_schema.conversion_api.to_hex(TRUNC(DBMS_RANDOM.VALUE( 1, 65535))), 4, '0') || my_schema.conversion_api.to_hex(TO_NUMBER(REPLACE(NVL(SYS_CONTEXT('USERENV','IP_ADDRESS'), '123.123.123.123'), '.', ''))), 12, '0') sss
FROM my_schema.MY_TABLE

Ответы [ 3 ]

6 голосов
/ 02 ноября 2011

Диагностика APC верна.Вы должны иметь энтропию в своем начальном генераторе случайных чисел.

Хотя в Oracle уже есть генератор уникальных идентификаторов, который SYS_GUID().

SELECT sys_guid(), name FROM my_table2;

Вы можете попробовать это, который производит 9 GUID:

SELECT sys_guid() from dual connect by level < 10;

Tora screenshot

Не пытайтесь заново изобретать колесо, когда оно уже существует.

4 голосов
/ 02 ноября 2011

Дело в том, что "случайный" на самом деле не случайный .Учитывая то же самое начальное значение для DBMS_RANDOM.INITIALISE(), последующие вызовы DBMS_RANDOM.VALUE() вернут тот же результат.Проверьте это:

SQL> exec DBMS_RANDOM.initialize (val => 1)

PL/SQL procedure successfully completed.

SQL> select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual
  2  /

TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
                                       49214

SQL> r
  1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual

TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
                                       56385

SQL> r
  1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual

TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
                                       23941

SQL> exec DBMS_RANDOM.initialize (val => 1)

PL/SQL procedure successfully completed.

SQL> select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual;

TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
                                       49214

SQL> r
  1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual

TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
                                       56385

SQL> r
  1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual

TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
                                       23941

SQL> 

Если мы посмотрим на код, который вы получили с сайта Тима, мы увидим следующую строку:

l_seed := TO_NUMBER(TO_CHAR(SYSDATE,'YYYYDDMMSS'));

Из чего мы можем предположить, что ваш процесс вставляет 10-11 строк в секунду:)

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

Хм, я сказал "гарантия".Ой-ой.Это по своей природе случайный, что он может дать тот же результат, что и два работает.Так что, возможно, это должно быть «для минимизации вероятности получения одинакового результата для каждой строки».

В качестве альтернативы удалите инициализацию из функции и вызовите ее перед началом массовых вставок.Возможно ли это, зависит исключительно от вашей бизнес-логики.


Демонстрация

Вот функция, которая генерирует «случайное» число:

create or replace function get_random_number 
    (p_seed in number := 0)
    return pls_integer
is
begin
    if p_seed = 0
    then
        DBMS_RANDOM.initialize (val => TO_NUMBER(TO_CHAR(SYSDATE,'YYYYDDMMSS')));
    else    
        DBMS_RANDOM.initialize (val => p_seed);
    end if;
    return TRUNC(DBMS_RANDOM.value(low => 1, high => 65535));
end;
/

Если мы вызываем его двадцать раз спараметр по умолчанию возвращает каждый раз одно и то же число:

SQL> select rownum
       , get_random_number
from   dual
connect by level <= 20
/
  2    3    4    5  
    ROWNUM GET_RANDOM_NUMBER
---------- -----------------
         1             10239
         2             10239
         3             10239
         4             10239
         5             10239
         6             10239
         7             10239
         8             10239
         9             10239
        10             10239
        11             10239
        12             10239
        13             10239
        14             10239
        15             10239
        16             10239
        17             10239
        18             10239
        19             10239
        20             10239

20 rows selected.

SQL> 

Принимая во внимание, что если мы передаем значение, оно использует разные начальные значения каждый раз, и вот!мы получаем другой результат:

SQL> select rownum
       , get_random_number(rownum)
from   dual
connect by level <= 20
/
  2    3    4    5  
    ROWNUM GET_RANDOM_NUMBER(ROWNUM)
---------- -------------------------
         1                     49214
         2                      6476
         3                     42426
         4                      2370
         5                     48546
         6                     52483
         7                      6964
         8                     46764
         9                     27569
        10                      7673
        11                     52446
        12                     50229
        13                     27861
        14                     31413
        15                     11518
        16                     13471
        17                     38766
        18                      9949
        19                     61656
        20                     25797

20 rows selected.

SQL>

Это работает, потому что передача в ROWNUM вызывает оценку функции для каждой строки.Не следует использовать ROWNUM в качестве начального числа в производственной системе: временные метки лучше.Или объедините дату и время с rownum, чтобы обеспечить уникальное начальное число для каждой строки.

1 голос
/ 02 ноября 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...