【问题标题】:Use function as default value for column in Oracle11g在Oracle11g中使用函数作为列的默认值
【发布时间】:2011-07-22 20:19:50
【问题描述】:

我们正在我的工作中测试 Oracle,我负责在 Oracle 上构建所有数据库对象(表、过程、触发器等),我们目前使用 Microsoft SQL Server 2008 R2。我们几乎所有的 ID 列都使用唯一标识符。我使用这个函数来创建 GUID:

CREATE OR REPLACE FUNCTION NEWID RETURN CHAR IS guid CHAR(36) ;
BEGIN
    SELECT SYS_GUID() INTO guid FROM DUAL;
    guid :=
               SUBSTR(guid,  1, 8) ||
        '-' || SUBSTR(guid,  9, 4) ||
        '-' || SUBSTR(guid, 13, 4) ||
        '-' || SUBSTR(guid, 17, 4) ||
        '-' || SUBSTR(guid, 21);
    RETURN guid;
END NEWID;
/

但现在我不知道如何在创建表时将其用作列的默认值。这是一个无效的示例:

CREATE TABLE "NonWorkingExample"
(   
  "ID"                              CHAR(36)      NOT NULL DEFAULT   NEWID(),
  "UnitNumber"                      NUMBER(38)    NOT NULL,
  "StartDateTime"                   TIMESTAMP     NOT NULL,
  "EndDateTime"                     TIMESTAMP     NULL,

  CONSTRAINT PK_RentalAgreements  PRIMARY KEY ("ID")
);

还有错误:

Error starting at line 1 in command:
CREATE TABLE "NonWorkingExample"
(   
  "ID"                          CHAR(36)      NOT NULL DEFAULT NEWID(),
  "UnitNumber"                  NUMBER(38)    NOT NULL,
  "StartDateTime"               TIMESTAMP     NOT NULL,
  "EndDateTime"                 TIMESTAMP     NULL,

  CONSTRAINT PK_RentalAgreements  PRIMARY KEY ("ID")
)
Error at Command Line:3 Column:58
Error report:
SQL Error: ORA-00907: missing right parenthesis
00907. 00000 -  "missing right parenthesis"
*Cause:    
*Action:

任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 顺便说一句,我无法让您的函数按所写的方式运行。它编译了文件,但select newid from dual 导致ORA-06502: PL/SQL: numeric or value error: character string buffer too small 将guid 的声明更改为varchar2(36) 修复了它。
  • 有趣。我从另一个 SO 帖子中获得了该功能

标签: sql oracle oracle11g


【解决方案1】:

我完全同意其他答案,如果你真的想调用一个函数,你需要使用触发器,或者你需要将函数调用嵌入到 INSERT 语句本身中。在INSERT 语句中嵌入函数调用比强制Oracle 为您插入的每一行执行触发器更有效。

不过,我应该指出,您可以单独使用 SYS_GUID() 作为列的默认值,而不必担心触发器

SQL> create table foo (
  2    col1 varchar2(32) default sys_guid(),
  3    col2 number
  4  );

Table created.

SQL> insert into foo( col2 ) values( 1 );

1 row created.

SQL> select * from foo;

COL1                                   COL2
-------------------------------- ----------
7B64E8AE7404421C80A590F65873CD79          1

您真的需要在您的 GUID 值中添加额外的破折号吗?您是否可以仅在显示数据时添加破折号?或者,由于您使用的是 11g,因此添加一个基于函数的虚拟列,将无破折号的 GUID 转换为您喜欢的格式的 GUID?

由于您来自 SQL Server,我应该指出,在 Oracle 中使用序列来填充合成主键而不是使用 GUID 是一种惯例。使用序列填充密钥通常比调用 SYS_GUID 更有效。

【讨论】:

  • 在插入中嵌入 PLSQL 函数仍会导致上下文转换。
  • @Shannon - 你是说你看到 SYS_GUID 返回重复值吗?这对我来说似乎不太可能——你有重现问题的代码吗?
  • 不,我说的不是 sys_guid。不过没关系,中间的指南是不同的,我在查看它们时不够仔细。
  • +1 尤其适用于建议序列和正常(无破折号)sys_guid
  • “在 GUID 值中使用破折号” - 或使用 UUID - 意味着使用符合 to the RFC standard 而不是 Oracle 的处理方式的值。
【解决方案2】:

建议:

  • 改用触发器。

例如:

CREATE TRIGGER SetGUIDforTableXYZ BEFORE INSERT ON TableXYZ
FOR EACH ROW
BEGIN
    :new.ID := NEWID();
END;

这应该可以解决问题(假设我没有在某处弄乱语法)。

【讨论】:

  • @thinkdevcode - 有效的解决方案比无效的解决方案性能更高:)
【解决方案3】:

正如@Kerri 暗示的那样,您不能将 PLSQL 函数用作表定义中的默认值。 Oracle documentation 中的相关语句是“A DEFAULT 表达式不能包含对 PL/SQL 函数的引用...”。

除了作为插入格式化 GUID 的唯一方法(正如您尝试做的那样)之外,触发器还提供第二个优势:与默认值不同,触发器设置的值不能被粗心的开发人员覆盖.

顺便说一句,你真的应该修改你的 newid 函数以使用直接赋值而不是 select ... from dual

CREATE OR REPLACE FUNCTION NEWID RETURN CHAR IS 
    guid VARCHAR(36);
BEGIN
    guid := SYS_GUID();
    guid :=
               SUBSTR(guid,  1, 8) ||
        '-' || SUBSTR(guid,  9, 4) ||
        '-' || SUBSTR(guid, 13, 4) ||
        '-' || SUBSTR(guid, 17, 4) ||
        '-' || SUBSTR(guid, 21);
    RETURN guid;
END NEWID;
/

最后,我建议你使用varchar2 而不是char。与popular opinion 相比,char 没有存储或效率优势,因此您不妨将varchar2 用于一切,只是为了简单起见。

【讨论】:

  • 感谢您的回复和重写的功能。
【解决方案4】:

You can't use a function for this。您看不到它,因为您的语法错误(CONSTRAINTS 之前的 DEFAULT)。你应该有:

"ID"    CHAR(36)  DEFAULT NEWID()   NOT NULL ,

此时您将收到以下消息 (11g):

SQL> create table tt (id varchar2(36) default newid() not null);
create table tt (id varchar2(36) default newid() not null)
                                   *
ERROR at line 1:
ORA-04044: procedure, function, package, or type is not allowed here


SQL>

正如@Kerri 所说,它需要一个触发器来自动执行此操作。

【讨论】:

    【解决方案5】:

    如何通过尝试这样的方式删除函数和触发器依赖项:

    CREATE TABLE "NonWorkingExample" 
    (    
      "ID"                          CHAR(36)      NOT NULL DEFAULT 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'), 
      "UnitNumber"                  NUMBER(38)    NOT NULL, 
      "StartDateTime"               TIMESTAMP     NOT NULL, 
      "EndDateTime"                 TIMESTAMP     NULL, 
    
      CONSTRAINT PK_RentalAgreements  PRIMARY KEY ("ID") 
    ) 
    

    【讨论】:

      【解决方案6】:

      如果 Oracle SYS_GUID() 的某些相似/顺序的 GUID 困扰您,您可以将 java.util.UUID.randomUUID() 包装在 pl/sql 函数中以获得更不可预测的值:

      CREATE OR REPLACE FUNCTION Java_Util_Random_UUID RETURN VARCHAR2 AS
      language java
      name 'java.util.UUID.randomUUID() return String';
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-16
        • 1970-01-01
        • 2010-12-17
        • 2019-11-29
        • 1970-01-01
        • 2019-09-11
        • 2020-11-07
        • 2012-08-01
        相关资源
        最近更新 更多