How to generate a version 4 (random) UUID on Oracle?

前端 未结 9 1089
野性不改
野性不改 2020-12-05 10:37

This blog explains, that the output of sys_guid() is not random for every system:

http://feuerthoughts.blogspot.de/2006/02/watch-out-for-sequential-orac

相关标签:
9条回答
  • 2020-12-05 11:31

    Accepted answer from ceving is inconsistent with RFC4122: the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved should be set to zero and one, respectively. That makes y equal to 8,9,a or b in already mentioned by uğur-yeşilyurt format xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx

    My solution made point blank along 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;
    

    EDIT:

    Although first implementation easy to understand it's rather inefficient. Next solution is 3 to 4 times faster.

    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;
    

    This test demostrates that random_uuid takes about one millisecond and random_uuid2 only 250 microseconds. Concatenation in the first version consumed too much time;

    declare
       dummy_uuid raw(16);
    begin
       for i in 1 .. 20000 loop
          --dummy_uuid := random_uuid;
          dummy_uuid := random_uuid2;
       end loop;
    end;
    
    0 讨论(0)
  • 2020-12-05 11:34

    there are some pure plsql functions written by me and one of my friend that generates uuid version 4 and formats any type of GUIDs. also formatters written in two way. one concating string and one use regex for formatting uuid

    CREATE OR REPLACE FUNCTION RANDOM_UUD_RAW
      RETURN RAW IS V_UUID RAW(16);
      BEGIN V_UUID := SYS.DBMS_CRYPTO.Randombytes(16);
        V_UUID := UTL_RAW.Overlay(UTL_RAW.Bit_or(UTL_RAW.Bit_and(UTL_RAW.Substr(V_UUID, 7, 1), '0F'), '40'), V_UUID, 7, 1);
        V_UUID := UTL_RAW.Overlay(UTL_RAW.Bit_or(UTL_RAW.Bit_and(UTL_RAW.Substr(V_UUID, 9, 1), '3F'), '80'), V_UUID, 9, 1);
        RETURN V_UUID;
      END RANDOM_UUD_RAW; --
    CREATE OR REPLACE FUNCTION UUID_FORMATTER_CONCAT(V_UUID RAW)
      RETURN VARCHAR2 IS V_STR VARCHAR2(36);
      BEGIN V_STR := lower(SUBSTR(V_UUID, 1, 8) || '-' || SUBSTR(V_UUID, 9, 4) || '-' || SUBSTR(V_UUID, 13, 4) || '-' || SUBSTR(V_UUID, 17, 4) || '-' || SUBSTR(V_UUID, 21));
        RETURN V_STR;
      END UUID_FORMATTER_CONCAT; --
    CREATE OR REPLACE FUNCTION UUID_FORMATTER_REGEX(V_UUID RAW)
      RETURN VARCHAR2 IS V_STR VARCHAR2(36);
      BEGIN V_STR := lower(regexp_replace(V_UUID, '(.{8})(.{4})(.{4})(.{4})(.{12})', '\1-\2-\3-\4-\5'));
        RETURN V_STR;
      END UUID_FORMATTER_REGEX; --
    CREATE OR REPLACE FUNCTION RANDOM_UUID_STR
      RETURN VARCHAR2 AS BEGIN RETURN UUID_FORMATTER_CONCAT(RANDOM_UUD_RAW());
      END RANDOM_UUID_STR; --
    CREATE OR REPLACE FUNCTION RANDOM_UUID_STR_REGEX
      RETURN VARCHAR2 AS BEGIN RETURN UUID_FORMATTER_REGEX(RANDOM_UUD_RAW());
      END RANDOM_UUID_STR_REGEX;
    
    
    
    0 讨论(0)
  • 2020-12-05 11:36

    The easiest and shortest way to get a Java-based function for me was:

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

    I can't completely understand why it does not compile if I add .toString() though.

    0 讨论(0)
提交回复
热议问题