【问题标题】:Creating a Rails 5 migration for foreign key(s) referencing their own table为引用自己的表的外键创建 Rails 5 迁移
【发布时间】:2023-03-31 16:43:02
【问题描述】:

我正在编写一个显示家谱的小狗数据库。 我有一个 postgresql 表“狗”,它有一个 dog_id、名字、重量等。 我还希望它有一个mother_id 和一个father_id 来存储另一个dog_id。

首先,这行得通吗? 我将如何为 Rails5 编写迁移?

或者我应该有另一张表“亲子关系”,只是将狗彼此联系起来?

【问题讨论】:

    标签: ruby-on-rails postgresql ruby-on-rails-5 rails-activerecord rails-migrations


    【解决方案1】:

    我不同意neongrau。您不需要第二张桌子来实现这一点。可以通过在您的狗表中进行动态自连接来完成。您将首先在单个模型中声明所有关系,并分别声明它们的外键:

    models/dog.rb:

    class Dog < ActiveRecord::Base
      belongs_to :mom, class_name: "Dog", foreign_key: "mom_id"
      belongs_to :dad, class_name: "Dog", foreign_key: "dad_id"
      has_many :kids, class_name: "Dog"
    end
    

    然后您将运行引用妈妈和爸爸的迁移:

    class CreateDogs < ActiveRecord::Migration
      def change
        create_table :dogs do |t|
          t.string :name
          t.integer :age
          t.references :mom
          t.references :dad
          t.integer :dog_id
    
          t.timestamps null: false
        end
    end
    

    现在我们通过手动建立关联来测试它:

       kid = Dog.create(name: "Rover")
        => #<Dog id: 8, name: "Rover"..>
    
       ma = Dog.create(name: "Susie")
       => #<Dog id: 2, name: "Susie"..>
    
       pa = Dog.create(name: "Doug")
       => #<Dog id: 8, name: "Doug"..>
    
    kid.mom_id = 2
    kid.save
    
    kid.mom
     => #<Dog id: 2, name: "Susie"...>
    
    kid.dad_id = 3
    kid.save
    
    kid.dad
     => #<Dog id: 3, name: "Doug"...>
    
    
    ma.kids << kid
    ma.save
    
    ma.kids
     => #<Dog id: 1, name: "Rover"...>
    
    pa.kids << kid 
    pa.save
    
    pa.kids
     => #<Dog id: 1, name: "Rover"...>
    

    现在你有一个动态的关系,狗可以有多个孩子,并且可能属于母亲和父亲。

    【讨论】:

    • 很高兴不同意你的观点;) STI 很好也很简单,但是当你需要遍历一棵树时,你对祖先的每个级别(妈妈和爸爸)都有一个 n+2 sql 查询问题.因此,对于曾曾祖父母,您已经使用 n+2+4+8 sql 查询来呈现家谱。网站性能将不存在。
    • 一切都可以缓存。您可以创建一个方法来预先加载 id 的嵌套,将其缓存并让迭代器使用它。
    • 我现在已经尝试过 MisterCal 的方式,效果非常好。我确实看到了neongrau 关于性能的观点,我将研究方便的ClosureTree 东西以进行重构。由于它目前只是在本地作为试用版运行,所以我不太担心性能。
    • 很好,带有相关父母的基本模型犬对此很好。您可以在闭包树中添加一个稍后将充当缓存的点。这就是所有不同层次树解决方案(嵌套集、相邻列表、闭包树)的用途。在闭包树中,您将为每只狗添加新根,并通过将父节点作为子节点添加到树中来反转方向。甚至父母也会在该树的不同级别多次存在,因此您可以在一个 sql 查询中读取任何狗的完整祖先系。
    • @neongrau 你真的不需要任何花哨或非规范化的东西来拉出一整棵树,PostgreSQL 支持WITH RECURSIVE 查询。
    【解决方案2】:

    一定要去至少第二张桌子。

    我建议添加一个配对或幼崽模型​​并将您的狗用它映射,因为饲养员可能会再次进行相同的配对。

    由于层次结构在使用更深的嵌套数据时很快就会出现性能问题,您可能会考虑将每只狗的祖先存储在另一个或两个表中,以便能够足够快地为网站检索它们。

    我建议https://github.com/ClosureTree/closure_tree,因为它在这种情况下提供了最佳性能(经常阅读,不经常写)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-19
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 1970-01-01
      • 2016-12-07
      • 2018-03-28
      • 2013-08-27
      相关资源
      最近更新 更多