Rails模型与外键本身

我有一个Oracle DB模式,其中包含一个“用户”表。 此表有两个非空外键给编辑器和创建者,它们也是用户。

架构转储如下所示:

create_table "users", :force => true do |t| t.integer "creator_id", :precision => 38, :scale => 0, :null => false t.integer "editor_id", :precision => 38, :scale => 0, :null => false end add_foreign_key "users", "users", :column => "creator_id", :name => "r_user_creatorid", :dependent => :nullify add_foreign_key "users", "users", :column => "editor_id", :name => "r_user_editorid", :dependent => :nullify 

我的用户模型如下所示:

 class User  "User" belongs_to :editor, :class_name => "User" validates_presence_of :creator, :editor end 

当我尝试保存第一个用户时出现问题。 还没有其他用户存在,但我没有null editor_id或creator_id。 如果我尝试将编辑器和创建器设置为自身,则会出现堆栈溢出。

从理论上讲,所有用户(第一个除外)都有创建者和编辑者。 有没有办法在不暂时删除非null约束的情况下完成此操作?

所以问题是,层次结构顶部必须有一个用户,没有经理的用户(在你的例子中为编辑器)。 这就是为什么这种结构的经典解决方案是允许空值。 您在结束段落中承认这一点:

“从理论上讲,所有用户(第一个除外)都有创建者和编辑器是有道理的。有没有办法在不暂时删除非空约束的情况下实现这一目的?”

踢球者是,如果第一个用户没有CREATOR或EDITOR那么就没有“临时”:你必须放弃强制约束。 如果这样做,递归外键约束的问题将消失。


另一种方法是介绍亚里士多德所谓的Prime Mover,一个创造者本身的用户。 鉴于此表:

 create table t72 ( userid number not null , creator number not null , editor number not null , constraint t72_pk primary key (userid) , constraint t72_cr_fk foreign key (creator) references t72 (userid) , constraint t72_ed_fk foreign key (editor) references t72 (userid) ) / 

创建这样的用户非常简单:

 SQL> insert into t72 values (1,1,1) 2 / 1 row created. SQL> commit; Commit complete. SQL> 

那么为什么这不是规范的解决方案呢。 好吧,它导致了一个稍微古怪的数据模型,一旦我们添加了更多的用户,就会对分层查询造成严重破坏。

 SQL> select lpad(' ', level-1)|| u.userid as userid 2 , u.name 3 , u.editor 4 from t72 u 5 connect by 6 prior userid = editor 7 start with userid=1 8 / ERROR: ORA-01436: CONNECT BY loop in user data no rows selected SQL> 

基本上数据库不喜欢USERID是它自己的编辑器。 但是,有一种解决方法,即NOCYCLE关键字(引入10g)。 这告诉数据库忽略层次结构中的循环引用:

 SQL> select lpad(' ', level-1)|| u.userid as userid 2 , u.name 3 , u.editor 4 from t72 u 5 connect by nocycle 6 prior userid = editor 7 start with userid=1 8 / USERID NAME EDITOR ---------- ---------- ---------- 1 ONE 1 2 TWO 1 3 THREE 2 4 FOUR 2 5 FIVE 2 6 SIX 2 7 SEVEN 6 7 rows selected. SQL> 

这没关系,因为数据仍然是正确的分层。 但是如果我们这样做会发生什么:

 SQL> update t72 set editor = 7 2 where userid = 1 3 / 1 row updated. SQL> 

我们失去了关系(1 – > 7)。 我们可以使用CONNECT_BY_ISNOCYCLE伪列来查看哪一行正在循环。

 SQL> select lpad(' ', level-1)|| u.userid as userid 2 , u.name 3 , u.editor 4 , connect_by_iscycle 5 from t72 u 6 connect by nocycle 7 prior userid = editor 8 start with userid=1 9 / USERID NAME EDITOR CONNECT_BY_ISCYCLE ---------- ---------- ---------- ------------------ 1 ONE 7 0 2 TWO 1 0 3 THREE 2 0 4 FOUR 2 0 5 FIVE 2 0 6 SIX 2 0 7 SEVEN 6 1 7 rows selected. SQL> 

Oracle具有许多附加function,可以更轻松地在纯SQL中使用分层数据。 这一切都在文档中。 了解更多 。

我原以为你会删除NOT NULL约束(即允许第一个用户为创建者和编辑者设置NULL)。

然后,您可以实现约束以确保所有后续条目都不为空,例如:

 CONSTRAINT creator_required CHECK (creator IS NOT NULL OR userid = 1) CONSTRAINT editor_required CHECK (editor IS NOT NULL OR userid = 1)