Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 外键指向自身的Rails模型_Ruby On Rails_Oracle_Database Design_Foreign Keys - Fatal编程技术网

Ruby on rails 外键指向自身的Rails模型

Ruby on rails 外键指向自身的Rails模型,ruby-on-rails,oracle,database-design,foreign-keys,Ruby On Rails,Oracle,Database Design,Foreign Keys,我有一个oracledb模式,其中包括一个“users”表。此表有两个指向编辑器和创建者的非空外键,它们也是用户 架构转储如下所示: create_table "users", :force => true do |t| t.integer "creator_id", :precision => 38, :scale => 0, :null => false t.integer "editor_id",

我有一个oracledb模式,其中包括一个“users”表。此表有两个指向编辑器和创建者的非空外键,它们也是用户

架构转储如下所示:

  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 < ActiveRecord::Base
  belongs_to :creator, :class_name => "User"
  belongs_to :editor, :class_name => "User"

  validates_presence_of :creator, :editor
end
class用户“用户”
属于:编辑器,:class\u name=>“用户”
验证:创建者,:编辑器的存在
结束
当我试图保存第一个用户时,问题就出现了。还不存在其他用户,但我不能有空的编辑器id或创建者id。如果我尝试将编辑器和创建者设置为自身,则会出现堆栈溢出


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

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

“从理论上讲,这一切都是有道理的 用户(第一个用户除外)具有 创作者和编辑,有什么办法吗 要做到这一点,不需要暂时 删除非空约束?”

更重要的是,如果第一个用户没有创建者或编辑器,那么就没有“临时”:您必须放弃强制约束。如果这样做,递归外键约束的问题将消失


另一种选择是引入亚里士多德所称的原动机,即创造者本身的用户。鉴于此表:

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有许多附加功能,可以更轻松地使用纯SQL中的分层数据。这些都在文件中

我认为您应该删除NOTNULL约束(即允许第一个用户为创建者和编辑器设置NULL)

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

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

像这样的鸡和蛋类型的问题让外键有点麻烦。大多数Rails应用程序都不会使用它们,从而避免了很多麻烦,并接受了不一致引用的潜在风险。