Rails模型与外键本身

Aar*_*son 1 oracle database-design ruby-on-rails foreign-keys

我有一个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
Run Code Online (Sandbox Code Playgroud)

我的用户模型如下所示:

class User < ActiveRecord::Base
  belongs_to :creator, :class_name => "User"
  belongs_to :editor, :class_name => "User"

  validates_presence_of :creator, :editor
end
Run Code Online (Sandbox Code Playgroud)

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

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

APC*_*APC 5

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

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

踢球者是,如果第一个用户没有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)
)
/
Run Code Online (Sandbox Code Playgroud)

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

SQL> insert into t72 values (1,1,1)
  2  /

1 row created.

SQL> commit;

Commit complete.

SQL>
Run Code Online (Sandbox Code Playgroud)

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

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> 
Run Code Online (Sandbox Code Playgroud)

基本上数据库不喜欢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>
Run Code Online (Sandbox Code Playgroud)

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

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

1 row updated.

SQL> 
Run Code Online (Sandbox Code Playgroud)

我们失去了一段感情(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>  
Run Code Online (Sandbox Code Playgroud)

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