在批量插入过程中正在缓存oracle函数调用

当插入批量行并使用函数调用作为列值之一时,我会从函数中获得每10-11行完全相同的值。 该函数实际上是生成UUID值并返回唯一结果。 如果我将insert语句中的函数调用替换为函数的实际代码,它永远不会重复。

所以我从这里得出的结论是,oracle实际上缓存了函数的结果,并且每插入一个10-11行就只调用一次。 我如何改变这种行为?

我打电话给我的功能来自http://www.oracle-base.com/articles/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

事情是,“随机”实际上不是随机的。 给定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> 

如果我们看一下从Tim网站上获得的代码,我们看到这一行:

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

从中我们可以推测,你的过程每秒插入10-11行:)

如果用SYSTIMESTAMP替换SYSDATE并将掩码更改为毫秒(或更小),则每次都应该获得不同的种子,因此每次都会有不同的值。 请注意,您仍然需要强制重新评估该函数,以确保每行获得不同的结果(请参见下面的演示)。

呃,我是否说“保证”? 嗯,哦。 它随机产生的结果可能会产生相同的结果。 所以也许应该是“尽量减少每行获得相同结果的机会”。

或者,从函数中删除初始化并在开始批量插入之前调用它。 这是否可行取决于您的业务逻辑。


示范

这是一个产生“随机”数字的函数:

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连接起来,为每一行提供一个唯一的种子。


APC的诊断是正确的。 你需要在你的随机发生器种子中有熵。

虽然,Oracle已经有一个唯一的ID生成器,它是SYS_GUID()

SELECT sys_guid(), name FROM my_table2;

你可以试试这个产生9个GUID的东西:

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

Tora截图

当它已经存在时,不要试图重新发明轮子。


还没有尝试过,但我相信甲骨文正在为你的new_uuid()函数计算一次值,并为每个返回的行输出值(就像你选择了systimestamp,不管从哪个方面来看......它会输出相同的时间戳所有行。

所以,你可以修改你的函数来从每一行(对于种子?)进行一些输入,或者只是使用序列。

链接地址: http://www.djcxy.com/p/91437.html

上一篇: oracle function call is being cached during bulk insert

下一篇: How to generate a new GUID?