sourcetip

Oracle에서 버전 4(랜덤) UUID를 생성하는 방법은 무엇입니까?

fileupload 2023. 6. 12. 21:50
반응형

Oracle에서 버전 4(랜덤) UUID를 생성하는 방법은 무엇입니까?

이 블로그는 다음과 같이 설명합니다.sys_guid()모든 시스템에 대해 랜덤하지 않습니다.

http://feuerthoughts.blogspot.de/2006/02/watch-out-for-sequential-oracle-guids.html

안타깝게도 저는 그런 시스템을 사용해야 합니다.

임의 UUID를 가져오는 방법은 무엇입니까? 합니까?sys_guid()Oracle에서 안정적으로 랜덤 UUID를 가져오는 방법이 없다면 어떻게 해야 합니까?

여기 @Pablo Santa Cruz의 답변과 당신이 게시한 코드를 바탕으로 한 완벽한 예가 있습니다.

저는 당신이 왜 에러 메시지를 받았는지 모르겠습니다.아마도 SQL Developer의 문제일 것입니다.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();
      }
   }
   /
Java created.
   CREATE OR REPLACE FUNCTION RandomUUID
   RETURN VARCHAR2
   AS LANGUAGE JAVA
   NAME 'RandomUUID.create() return java.lang.String';
   /
Function created.
   select randomUUID() from dual;
RANDOMUUID()
--------------------------------------------------------------
4d3c8bdd-5379-4aeb-bc56-fcb01eb7cc33

하지만 저는 계속할 것입니다.SYS_GUIDSupport에서 하십시오. 이는 11된 것으로 됩니다.My Oracle Support 서 ID 1371805.1을 확인하십시오. 이 버그는 11.2.0.3에서 수정된 것으로 추정됩니다.

편집

어떤 것이 더 빠른지는 기능이 어떻게 사용되는지에 따라 다릅니다.

자바 버전은 SQL에서 사용할 때 약간 더 빠른 것 같습니다.그러나 PL/SQL 컨텍스트에서 이 기능을 사용할 경우 PL/SQL 기능이 약 2배 더 빠릅니다. (엔진 간 전환의 오버헤드를 방지하기 때문일 수 있습니다.)

다음은 간단한 예입니다.

--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;
/

버전 4 GUID는 완전히 랜덤하지 않습니다.일부 바이트는 수정되어야 합니다.https://www.cryptosys.net/pki/uuid-rfc4122.html 따르면 이 작업이 왜 수행되었는지 또는 중요한지는 잘 모르겠습니다.

버전 4 UUID를 생성하는 절차는 다음과 같습니다.

Generate 16 random bytes (=128 bits)
Adjust certain bits according to RFC 4122 section 4.4 as follows:
    set the four most significant bits of the 7th byte to 0100'B, so the high nibble is "4"
    set the two most significant bits of the 9th byte to 10'B, so the high nibble will be one of "8", "9", "A", or "B".
Encode the adjusted bytes as 32 hexadecimal digits
Add four hyphen "-" characters to obtain blocks of 8, 4, 4, 4 and 12 hex digits
Output the resulting 36-character string "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

Java 버전의 값이 표준을 준수하는 것으로 나타납니다.

https://stackoverflow.com/a/10899320/1194307

다음 함수는 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_crypto 패키지를 생성하고 승인할 필요가 없습니다.

이제 이 문제를 해결 방법으로 사용합니다.

함수 random_uuid 반환 RAW를 생성하거나 대체합니다.v_uuid RAW(16);시작한다.v_vmdk : = sys.dbms_vmdk.dbbytes(16);return(utl_raw.dll(utl_raw).bit_or(utl_raw).bit_and (utl_raw.substr(v_uuid, 7, 1), '0F'), '40', v_uuid, 7);end random_details;

이 기능은 다음을 필요로 합니다.dbms_crypto그리고.utl_raw둘 다 실행 허가가 필요합니다.

grant execute on sys.dbms_crypto to uuid_user;

Java 기반 기능을 사용할 수 있는 가장 쉽고 짧은 방법은 다음과 같습니다.

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

추가하면 컴파일이 안 되는 이유를 완전히 이해할 수 없습니다..toString()그래도.

위의 답변 중 어느 것에도 완전히 만족하지 못했습니다. 자바가 설치되지 않은 경우가 많습니다.dbms_crypto보조금이 필요하고,sys_guid()순차적입니다.

저는 이것으로 결정했습니다.

create or replace function random_uuid return VARCHAR2 is
   random_hex varchar2(32);
begin
  random_hex := translate(DBMS_RANDOM.string('l', 32), 'ghijklmnopqrstuvwxyz', '0123456789abcdef0123');
  return          substr(random_hex, 1, 8) 
        || '-' || substr(random_hex, 9, 4)  
        || '-' || substr(random_hex, 13, 4)  
        || '-' || substr(random_hex, 17, 4)  
        || '-' || substr(random_hex, 21, 12);
end random_uuid;
/

dbms_random는 (기본적으로) 공용이므로 보조금이 필요하지 않으며 상당히 임의적입니다.참고:dbms_random암호화된 보안 기능이 없으므로 필요한 경우dbms_crypto위에 접근합니다.16진수 값 분포도 변환 함수에 의해 왜곡됩니다.실제 UUID 4 출력이 필요한 경우 서브스트를 조정할 수 있습니다(단, 고유성이 필요합니다).

동일한 기술을 SQL에서 사용할 수 있으며, 일부 상상력을 사용하는 함수는 없습니다.

select 
          substr(rand, 1, 8) 
|| '-' || substr(rand, 9, 4)  
|| '-' || substr(rand, 13, 4)  
|| '-' || substr(rand, 17, 4)  
|| '-' || substr(rand, 21, 12)  
from (select translate(DBMS_RANDOM.string('l', 32), 'ghijklmnopqrstuvwxyz', '0123456789abcdef0123') rand  from dual);

Java 프로시저를 작성하고 이를 컴파일하여 Oracle 내에서 실행할 수 있습니다.이 절차에서는 다음을 사용할 수 있습니다.

UUID uuid = UUID.randomUUID();
return uuid.toString();

원하는 값을 생성합니다.

Oracle에서 Java 프로시저를 컴파일하는 방법에 대한 링크입니다.

고유하지 않을 수 있지만 "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의 소스에서 수정되었습니다.

UUID 버전 4에 따라 형식은 xxxxxxxxxx-xxxxx-4xxxxx-yxxxxxxx-xxxxxxx이어야 합니다.@chevcat 응답은 이 형식을 제공하며, @ceving 응답은 버전 4 요구 사항을 부분적으로 제공합니다.결측 부품이 형식입니다. y는 8, 9, a 또는 b 중 하나여야 합니다.

이러한 답변을 혼합하고 해당 부분을 수정한 후 코드는 다음과 같습니다.

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;

ceveing에서 승인된 응답은 RFC4122와 일치하지 않습니다. clock_seq_hi_and_reserved의 가장 중요한 두 비트(비트 6 및 7)는 각각 0 및 1로 설정되어야 합니다.따라서 y는 이미 uğur-yesshilurt 형식 xxxxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

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는 약 1밀리초, 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;

저와 제 친구 중 한 명이 UUID 버전 4를 생성하고 모든 유형의 GUID를 포맷하는 순수한 plsql 함수가 있습니다.또한 두 가지 방식으로 작성된 사항에 대해서도.UUID 포맷을 위한 하나의 연결 문자열과 하나의 사용 regex

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;


언급URL : https://stackoverflow.com/questions/13951576/how-to-generate-a-version-4-random-uuid-on-oracle

반응형