【问题标题】:Can I have two foreign key from one table to an other, in order to get two columns field?我可以从一个表到另一个表有两个外键,以获得两列字段吗?
【发布时间】:2020-04-21 16:36:31
【问题描述】:

我有两张桌子,一个是 Player,另一个是 Result。我可以在 Result 上设置两个与 Player 相关的外键,以便我可以使用 Result 更改两个玩家的值吗?我正在开发 Ruby Web 应用程序,我将根据我要添加的与足球比赛相关的结果更新每个 Player 的字段。例如,如果得分是 Player1 3 - 0 Player2,则 Player1 将获得 3 分,而另一个 0,所以基本上从一个结果我将不得不更改表 Player 中的两个字段。我还必须添加每个玩家的GoalsScored 和GoalsTaken。我可以通过在结果中分配两个名称来解决这个问题,我将检查 Player 表中的名称,然后使用两个外键更改字段,或者我只需要正确设置我的方法?

这是表格:

Player
t.string :name
      t.integer :win
      t.integer :draw
      t.integer :lose
      t.integer :gs
      t.integer :gt
      t.integer :dr
      t.integer :points

Result
t.string :name1
      t.integer :goal1
      t.string :name2
      t.integer :goal2
      t.datetime :date
      t.references :player, null: false, foreign_key: true

如果我添加两个对 Result 的引用可以吗?

【问题讨论】:

  • 谁的名字是name1name2
  • 球员的名字。所以结果显示为name1(player1)goal1 -goal2 name2(player2)

标签: sql ruby database ruby-on-rails-5


【解决方案1】:

是的,你可以。有时这是个好主意,但不在这里。您需要一个连接表。原因如下。

您需要使用两个不同的列名并告诉 Rails 他们引用了哪个表。

Result
  t.string :name1
  t.integer :goal1
  t.string :name2
  t.integer :goal2
  t.datetime :date
  t.references :player1,
    null: false,
    foreign_key: { to_table: :players }
  t.references :player2,
    null: false,
    foreign_key: { to_table: :players }

而且您需要在代码中明确说明它们。

class Result < ApplicationRecord
  belongs_to :player1, class_name: 'Player'
  belongs_to :player2, class_name: 'Player'
end

在 Player 中将它们捆绑在一起有点小技巧。天真的做法是这样的。

class Player < ApplicationRecord
  has_many :player1_results,
    class_name: 'Result',
    foreign_key: :player1_id
  has_many :player2_results,
    class_name: 'Result',
    foreign_key: :player2_id
end

如果您想要所有 Player 的结果怎么办?如果你想要一个结果的所有玩家怎么办?有问题。您需要进行冗余查询或添加额外的子句,例如where player1_id = :player_id or player2_id = :player_id。同样name1name2goal1goal2


当您想要存储多个关联的事物时,您需要一个连接表。哪怕只有两个。它让生活更轻松。

你有一个结果表,但结果是什么?一场比赛!有关比赛的信息存储在哪里?在结果表中。它应该是它自己的表。

我们有三张桌子。球员、比赛和一个表格,用于存储球员在比赛中的表现(结果)。

create_table :players do
  t.string :name, null: fase
  ...
  t.timestamps
end

create_table :matches do
  t.string :name, null: false
  t.datetime :date, null: false
  ...
  t.timestamps
end

create_table :results do
  t.references :player, foreign_key: true, null: false
  t.references :match, foreign_key: true, null: false
  t.integer :goals
end

现在有了所有三个部分,我们可以将它们放在一起。比赛和球员是相关的结果。

class Matches < ApplicationRecord
  has_many :results
  has_many :players, through: :results
end

class Players < ApplicationRecord
  has_many :results
  has_many :matches, through: :results
end

class Results < ApplicationRecord
  belongs_to :match
  belongs_to :player
end

现在如果你想在你的比赛中找到球员......

players = match.players

这将为您执行结果连接。

如果您必须跟踪谁是玩家 1 和玩家 2,请将其添加到具有唯一约束的 Result 表中。

create_table :results do
  t.references :player, foreign_key: true, null: false
  t.references :match, foreign_key: true, null: false
  t.integer :goals, null: false, default: 0
  t.integer :player_number, null: false, default 1

  # Can't have two player 1s for the same match.
  # :player_number is deliberately first so this index also serves
  # to index player_number.
  t.index [:player_number, :match_id], unique: true
end

那么你可以像这样得到玩家1:

player = match.players.find_by!(player_number: 1)

你可以给关系添加一些方便的方法。

class Matches < ApplicationRecord
  has_many :results
  has_many :players, through: :results do
    def player(num)
      match.players.find_by!(player_number: num)
    end
  end
end

player = match.players.player(1)

【讨论】:

  • 非常感谢您的回复,描述性非常好。那么,如果我想根据游戏结果更改字段,例如更新每个玩家的胜利,我应该在结果中定义一个方法,以便自动更新 Player 类中的字段吗?我对 Ruby 还很陌生,但仍在尝试弄清楚一切。
  • 您可以使用 callback 执行此操作,但您可能希望在比赛中执行此操作,因为它知道两个结果并且可以确定谁赢了。
  • 我应该在 Match 中放什么?我仍然很困惑,我认为这很清楚,但是我阅读您的答案越多,对我来说就越复杂。如果我有 2 个结果的 1 个 matchID 必须让每个球员的每个进球都得分,我要在 Match 中存储什么?
  • 连接表,匹配还是结果?
  • 结果是连接表。它加入了与玩家的比赛。结果存储有关特定球员和比赛的任何信息:进球、犯规等。比赛存储诸如比赛名称、比赛时间和比赛地点等信息。如果你想存储匹配的结果,你可以使用match.results.create!( player: player1, goals: 3, player_number: 1); match.results.create!( player: player2, goals: 0, player_number: 2); 这会执行类似insert into results (player_id, goals, player_number) values (42, 3, 1); insert into results (player_id, goals, player_number) values (23, 0, 2) 的查询。
猜你喜欢
  • 1970-01-01
  • 2019-03-10
  • 2011-11-19
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
  • 2010-09-25
  • 2020-10-02
  • 2015-09-01
相关资源
最近更新 更多