簡體   English   中英

帶有外鍵的Rails模型

[英]Rails model with foreign key to itself

我有一個包含“用戶”表的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 < ActiveRecord::Base
  belongs_to :creator, :class_name => "User"
  belongs_to :editor, :class_name => "User"

  validates_presence_of :creator, :editor
end

當我嘗試保存第一個用戶時出現問題。 還沒有其他用戶,但是我不能有一個null的editor_id或creator_id。 如果嘗試將編輯器和創建器設置為自身,則會出現堆棧溢出。

從理論上講,所有用戶(第一個用戶除外)都具有創建者和編輯者是有意義的。 有什么方法可以在不臨時刪除非null約束的情況下完成此操作?

因此,問題在於,必須在層次結構的頂部有一個用戶,這個用戶沒有管理員(在您的示例中為編輯器)。 這就是為什么這種結構的經典解決方案是允許空值的原因。 您在結束語段中承認了這一點:

“從理論上講,所有用戶(第一個用戶除外)都具有創建者和編輯者是有道理的。在不臨時消除非null約束的情況下,有什么方法可以做到這一點?”

更重要的是,如果第一個用戶沒有CREATOR或EDITOR,那么就沒有“臨時”:您必須放棄強制性約束。 如果您這樣做,遞歸外鍵約束的問題將消失。


另一種方法是介紹亞里斯多德所說的“原動力”,即“創造者”本身的用戶。 給定此表:

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的分層數據更容易使用。 全部在文檔中。 了解更多

我以為您會刪除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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM