如何在Oracle上生成版本4(随机)UUID?

如何在Oracle上生成版本4(随机)UUID?,oracle,uuid,Oracle,Uuid,本博客解释说,sys\u guid()的输出对于每个系统来说都不是随机的: 不幸的是,我不得不使用这样的系统 如何确保获得随机UUID?是否可以使用sys\u guid()?如果不知道如何在Oracle上可靠地获取随机UUID?您可以编写一个Java过程,然后编译并在Oracle内部运行它。在该过程中,您可以使用: UUID uuid = UUID.randomUUID(); return uuid.toString(); 生成所需的值 有关如何在Oracle中编译java过程的链接。我现在

本博客解释说,
sys\u guid()
的输出对于每个系统来说都不是随机的:

不幸的是,我不得不使用这样的系统


如何确保获得随机UUID?是否可以使用
sys\u guid()
?如果不知道如何在Oracle上可靠地获取随机UUID?

您可以编写一个Java过程,然后编译并在Oracle内部运行它。在该过程中,您可以使用:

UUID uuid = UUID.randomUUID();
return uuid.toString();
生成所需的值


有关如何在Oracle中编译java过程的链接。

我现在将此作为一种解决方法:

创建或替换函数random_uuid返回原始值 v_uuid原始(16); 开始 v_uuid:=sys.dbms_crypto.randombytes(16); 返回(utl_raw.overlay(utl_raw.bit_或(utl_raw.bit_和(utl_raw.substr(v_uuid,7,1),'0F'),'40'),v_uuid,7)); 结束随机分组; 该函数需要
dbms\u crypto
utl\u raw
。两者都需要执行授权

grant execute on sys.dbms_crypto to uuid_user;

下面是一个完整的例子,基于@Pablo Santa Cruz的答案和您发布的代码

我不知道你为什么会收到错误消息。这可能是SQL开发人员的问题。在SQL*Plus中运行并添加函数后,一切正常:

   create or replace and compile
   java source named "RandomUUID"
   as
   public class RandomUUID
   {
      public static String create()
      {
              return java.util.UUID.randomUUID().toString();
      }
   }
   /

但如果可能的话,我会坚持使用
SYS\u GUID
。看看我的Oracle支持上的ID1371805.1——这个错误应该在11.2.0.3中修复

编辑

哪个更快取决于函数的使用方式

在SQL中使用Java版本时,它看起来稍微快一点。但是,如果要在PL/SQL上下文中使用此函数,则PL/SQL函数是 快一倍。(可能是因为它避免了在引擎之间切换的开销。)

下面是一个简单的例子:

--Create simple table
create table test1(a number);
insert into test1 select level from dual connect by level <= 100000;
commit;

--SQL Context: Java function is slightly faster
--
--PL/SQL: 2.979, 2.979, 2.964 seconds
--Java: 2.48, 2.465, 2.481 seconds
select count(*)
from test1
--where to_char(a) > random_uuid() --PL/SQL
where to_char(a) > RandomUUID() --Java
;

--PL/SQL Context: PL/SQL function is about twice as fast
--
--PL/SQL: 0.234, 0.218, 0.234
--Java: 0.52, 0.515, 0.53
declare
    v_test1 raw(30);
    v_test2 varchar2(36);
begin
    for i in 1 .. 10000 loop
        --v_test1 := random_uuid; --PL/SQL
        v_test2 := RandomUUID; --Java
    end loop;
end;
/
Java版本的值似乎符合标准。

以下函数使用sys_guid()并将其转换为uuid格式:

create or replace function random_uuid return VARCHAR2 is
  v_uuid VARCHAR2(40);
begin
  select regexp_replace(rawtohex(sys_guid()), '([A-F0-9]{8})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{12})', '\1-\2-\3-\4-\5') into v_uuid from dual;
  return v_uuid;
end random_uuid;

它不需要创建dbms_加密包并授予它

它可能不是唯一的,但会生成一个“类似GUID”的随机字符串:

 FUNCTION RANDOM_GUID
    RETURN VARCHAR2 IS
    RNG    NUMBER;
    N      BINARY_INTEGER;
    CCS    VARCHAR2 (128);
    XSTR   VARCHAR2 (4000) := NULL;
  BEGIN
    CCS := '0123456789' || 'ABCDEF';
    RNG := 15;

    FOR I IN 1 .. 32 LOOP
      N := TRUNC (RNG * DBMS_RANDOM.VALUE) + 1;
      XSTR := XSTR || SUBSTR (CCS, N, 1);
    END LOOP;

    RETURN XSTR;
  END RANDOM_GUID;

改编自DBMS_RANDOM.STRING的源代码。

为我获得基于Java的函数最简单、最短的方法是:

create or replace function random_uuid return varchar2 as
language java
name 'java.util.UUID.randomUUID() return String';

我无法完全理解,如果我添加
.toString()
,为什么它不会编译。

根据UUID版本4,格式应该是xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxxxx。回答提供此格式,也回答部分提供版本4要求。缺少的部分是格式y,y应该是8、9、a或b中的一个

混合这些答案并修复y部分后,代码如下所示:

create or replace function fn_uuid return varchar2 is
  /* UUID Version 4 must be formatted as xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal character (lower case only) and y is one of 8, 9, a, or b.*/

  v_uuid_raw raw(16);
  v_uuid     varchar2(36);
  v_y        varchar2(1);
begin

  v_uuid_raw := sys.dbms_crypto.randombytes(16);
  v_uuid_raw := utl_raw.overlay(utl_raw.bit_or(utl_raw.bit_and(utl_raw.substr(v_uuid_raw, 7, 1), '0F'), '40'), v_uuid_raw, 7);

  v_y := case round(dbms_random.value(1, 4))
            when 1 then
             '8'
            when 2 then
             '9'
            when 3 then
             'a'
            when 4 then
             'b'
           end;

  v_uuid_raw := utl_raw.overlay(utl_raw.bit_or(utl_raw.bit_and(utl_raw.substr(v_uuid_raw, 9, 1), '0F'), v_y || '0'), v_uuid_raw, 9);
  v_uuid     := regexp_replace(lower(v_uuid_raw), '([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{12})', '\1-\2-\3-\4-\5');

  return v_uuid;
end fn_uuid;

ceving接受的答案与以下内容不一致:时钟序列的两个最高有效位(位6和位7)和保留的两个最高有效位应分别设置为0和1。这使得y等于8,9,a或b,格式为uğur yeşilyurt已经提到的xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxxxx

我的解决方案在RFC上直截了当:

create or replace function random_uuid return raw is
  /*
  Set the four most significant bits (bits 12 through 15) of the
      time_hi_and_version field to the 4-bit version number from
      Section 4.1.3.
  */
  v_time_hi_and_version raw(2) := utl_raw.bit_and(utl_raw.bit_or(dbms_crypto.randombytes(2), '4000'), '4FFF');
  /*
  Set the two most significant bits (bits 6 and 7) of the
      clock_seq_hi_and_reserved to zero and one, respectively.
  */
  v_clock_seq_hi_and_reserved raw(1) := utl_raw.bit_and(utl_raw.bit_or(dbms_crypto.randombytes(1), '80'), 'BF');
  /*
  Set all the other bits to randomly (or pseudo-randomly) chosen
      values.
  */
  v_time raw(6) := dbms_crypto.randombytes(6);
  v_clock_seq_low_and_node raw(7) := dbms_crypto.randombytes(7);
begin
  return v_time || v_time_hi_and_version || v_clock_seq_hi_and_reserved || v_clock_seq_low_and_node;
end random_uuid;
编辑:

虽然第一个实现很容易理解,但效率很低。下一个解决方案的速度要快3到4倍

create or replace function random_uuid2 return raw is
  v_uuid raw(16) := dbms_crypto.randombytes(16);
begin
   v_uuid :=  utl_raw.bit_or(v_uuid, '00000000000040008000000000000000');
   v_uuid := utl_raw.bit_and(v_uuid, 'FFFFFFFFFFFF4FFFBFFFFFFFFFFFFFFF');
  return v_uuid;
end;
这项测试表明,random_uuid大约需要一毫秒,random_uuid2只需要250微秒。第一个版本中的连接占用了太多的时间

declare
   dummy_uuid raw(16);
begin
   for i in 1 .. 20000 loop
      --dummy_uuid := random_uuid;
      dummy_uuid := random_uuid2;
   end loop;
end;

我和我的一个朋友编写了一些纯plsql函数,它们生成uuid版本4并格式化任何类型的GUI。也可以用两种方式编写格式化程序。一个concating字符串和一个use regex用于格式化uuid

创建或替换随机函数
返回原始值为V_UUID原始值(16);
BEGIN V_UUID:=SYS.DBMS_CRYPTO.Randombytes(16);
V_UUID:=UTL_RAW.Overlay(UTL_RAW.Bit_或(UTL_RAW.Bit_和(UTL_RAW.Substr)(V_UUID,7,1),'0F'),'40'),V_UUID,7,1);
V_UUID:=UTL_RAW.Overlay(UTL_RAW.Bit_或(UTL_RAW.Bit_和(UTL_RAW.Substr)(V_UUID,9,1),'3F'),'80'),V_UUID,9,1);
返回V_UUID;
结束随机测试--
创建或替换函数UUID\u格式化程序\u CONCAT(V\u UUID原始)
返回VARCHAR2是V_STR VARCHAR2(36);
从V|UUID开始:=下(SUBSTR(V|UUID,1,8)| |-| | SUBSTR(V|UUID,9,4)|-| | SUBSTR(V|UUID,13,4)|-| | SUBSTR(V|UUID,17,4)|-| | SUBSTR;
返回V_STR;
终端UUID_格式化程序_CONCAT--
创建或替换函数UUID_格式化程序_REGEX(V_UUID RAW)
返回VARCHAR2是V_STR VARCHAR2(36);
BEGIN V_STR:=lower(regexp_replace(V_UUID),(.8})(.4})(.4})(.4})(.12}),\1-\2-\3-\4-\5');
返回V_STR;
结束UUID_格式化程序_正则表达式--
创建或替换函数RANDOM\u UUID\u STR
返回VARCHAR2作为开始返回UUID_格式化程序_CONCAT(RANDOM_UUD_RAW());
结束随机序列--
创建或替换函数RANDOM_UUID_STR_REGEX
返回VARCHAR2作为开始返回UUID_格式化程序_REGEX(RANDOM_UUD_RAW());
结束随机序列;

是否有可用的示例?是的,请查看我刚刚发布的链接。Oracle的数据库在其上下文上有一个Java虚拟机,可以在其上下文上运行这种Java代码,而无需运行外部程序。不起作用。当我尝试使用UUID时,我的Oracle拒绝编译它:我问如何在Oracle的PL/SQL中使用java.util.UUID,但到目前为止没有答案:如何找出调用java函数是否比使用我在工作中使用的Oracles dbms_加密包快?我的Solaris系统上的UID是可预测的,这是不允许的应用程序。@Jon Heller我尝试了SYS\u GUID(),但结果不是我所期望的。UUID的版本4应该看起来像xxxxxxxxxxxxx-4xxx-yxxx-xxxxxxxxxx或?@ZerOne SYS\u GUID()似乎工作正常,请参阅编辑。@JonHeller感谢您的编辑。也许我误解了你的意思,但你的意思是它以他们自己的方式正确工作吗?因为它不像RFCThanks所要求的格式,但是
regexp\u replace
听起来相当昂贵。UUID生成需要非常快。此功能不会在所有平台上都是随机的。例如,在Solaris上,每次执行时只有一个或两个字符发生更改。Cou
 FUNCTION RANDOM_GUID
    RETURN VARCHAR2 IS
    RNG    NUMBER;
    N      BINARY_INTEGER;
    CCS    VARCHAR2 (128);
    XSTR   VARCHAR2 (4000) := NULL;
  BEGIN
    CCS := '0123456789' || 'ABCDEF';
    RNG := 15;

    FOR I IN 1 .. 32 LOOP
      N := TRUNC (RNG * DBMS_RANDOM.VALUE) + 1;
      XSTR := XSTR || SUBSTR (CCS, N, 1);
    END LOOP;

    RETURN XSTR;
  END RANDOM_GUID;
create or replace function random_uuid return varchar2 as
language java
name 'java.util.UUID.randomUUID() return String';
create or replace function fn_uuid return varchar2 is
  /* UUID Version 4 must be formatted as xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal character (lower case only) and y is one of 8, 9, a, or b.*/

  v_uuid_raw raw(16);
  v_uuid     varchar2(36);
  v_y        varchar2(1);
begin

  v_uuid_raw := sys.dbms_crypto.randombytes(16);
  v_uuid_raw := utl_raw.overlay(utl_raw.bit_or(utl_raw.bit_and(utl_raw.substr(v_uuid_raw, 7, 1), '0F'), '40'), v_uuid_raw, 7);

  v_y := case round(dbms_random.value(1, 4))
            when 1 then
             '8'
            when 2 then
             '9'
            when 3 then
             'a'
            when 4 then
             'b'
           end;

  v_uuid_raw := utl_raw.overlay(utl_raw.bit_or(utl_raw.bit_and(utl_raw.substr(v_uuid_raw, 9, 1), '0F'), v_y || '0'), v_uuid_raw, 9);
  v_uuid     := regexp_replace(lower(v_uuid_raw), '([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{12})', '\1-\2-\3-\4-\5');

  return v_uuid;
end fn_uuid;
create or replace function random_uuid return raw is
  /*
  Set the four most significant bits (bits 12 through 15) of the
      time_hi_and_version field to the 4-bit version number from
      Section 4.1.3.
  */
  v_time_hi_and_version raw(2) := utl_raw.bit_and(utl_raw.bit_or(dbms_crypto.randombytes(2), '4000'), '4FFF');
  /*
  Set the two most significant bits (bits 6 and 7) of the
      clock_seq_hi_and_reserved to zero and one, respectively.
  */
  v_clock_seq_hi_and_reserved raw(1) := utl_raw.bit_and(utl_raw.bit_or(dbms_crypto.randombytes(1), '80'), 'BF');
  /*
  Set all the other bits to randomly (or pseudo-randomly) chosen
      values.
  */
  v_time raw(6) := dbms_crypto.randombytes(6);
  v_clock_seq_low_and_node raw(7) := dbms_crypto.randombytes(7);
begin
  return v_time || v_time_hi_and_version || v_clock_seq_hi_and_reserved || v_clock_seq_low_and_node;
end random_uuid;
create or replace function random_uuid2 return raw is
  v_uuid raw(16) := dbms_crypto.randombytes(16);
begin
   v_uuid :=  utl_raw.bit_or(v_uuid, '00000000000040008000000000000000');
   v_uuid := utl_raw.bit_and(v_uuid, 'FFFFFFFFFFFF4FFFBFFFFFFFFFFFFFFF');
  return v_uuid;
end;
declare
   dummy_uuid raw(16);
begin
   for i in 1 .. 20000 loop
      --dummy_uuid := random_uuid;
      dummy_uuid := random_uuid2;
   end loop;
end;