将主旗放在一对多关系中的位置

时间:2012-07-20 12:01:37

标签: mysql sql ruby-on-rails-3 postgresql

我遇到了这个问题:

  • 用户有很多糖果( 1:M ),
  • 其中一个糖果可以有主要
  • 标志
  • 每个用户只有一个糖果可以是主要的

哪种方式更适合做主旗?

解决方法1:

用户

| id  | name |
| 1   | Joe  |

糖果

| id | user_id | primary |
| 1  |  1      | true    |  
| 2  |  2      | false   |

溶液2:

用户

| id  | name | primary_candy_id |
| 1   | Joe  | 2                |

糖果

| id | user_id |
| 1  |  1      |
| 2  |  2      |

对Soluion 1的说明

  • 我得到了与糖果糖果表相关的一切
  • 如果我检查是否有任何Candy是主要的(不必加入)
  • ,请加快查询速度
  • 如果用户获得100个糖果,这样他就有99个 false ,只有1个有意义的 true => 99个不必要的列记录

解决方案2的注释

  • 这样我只有一个列记录(另外99个保存)
  • 如果我想检查Candy是否是主要的,我需要在客户端表上加入
  • 让我说我会介绍更多1:带有与用户相关的主要标志的M表:冰淇淋,巧克力,垃圾食品......这样我在用户中还有4个列

这是更多的SQL问题,但我在项目中使用Rails 3. *所以这个领域的最佳实践意见欢迎

3 个答案:

答案 0 :(得分:1)

我会创建一个名为UserCandy的类型的第三个模型,然后使数据库表看起来像这样:

# users
| id | name |
| 1  | Joe  |

# candies
| id | name    |
| 1  | foo bar |

# user_candies
| id | user_id | candy_id |
| 1  | 1       | 1        |

# primary_candies
| id | user_candy_id |
| 1  | 1             |

然后我会在用户和糖果之间建立多对多的关系,因为可能有多个人喜欢<insert some special type of candy here>

class User < ActiveRecord::Base
  has_many :user_candies
  has_many :candies, :through => :user_candies

  def primary_candy
    Candy.primary_for(self).first
  end
end

class Candy < ActiveRecord::Base
  has_many :user_candies
  has_many :users, :through => :user_candies

  scope :primary, joins(:user_candies => [:primary_user_candy, :user])
  scope :primary_for, lambda {|user| primary.where('users.id' => user.id) }
end

class UserCandy < ActiveRecord::Base
  belongs_to :user
  belongs_to :candy
  has_one :primary_candy
end

class PrimaryCandy < ActiveRecord::Base
  belongs_to :user_candy

  validate do
    if user_candy.user.primary_candy
      errors.add(:base, "User already has a primary  candy")
    end
  end
end

这样,当您添加不同类型的小吃时,您不必向users表添加列,而且,由于用户可能有也可能没有喜欢的糖果,您将避免用户中的空字段这样表。

您也可以通过这种方式重复使用不同的糖果类型。

因此,从关系数据库的角度来看,我的简短回答是:我没有旗帜,我会把主要糖果放在一个单独的模型中。

那就是说,你也可以使用三个表版本,只需将主标志放在user_candies表中。

我使用上面的模型代码制作了一个示例rails项目,可用here

答案 1 :(得分:0)

如何使用has_one关联。这样就可以消除两者中不必要的列,只有在需要时才能轻松访问User.primary_candy。

User :has_many candy
User :has_one candy :through => primary_candy 

Candy :belongs_to user
Candy :has_many primary_candy

Primary_candy :belongs_to User
Primary_candy :belongs_to Candy
Primary_candy :validates_uniqueness_of :user_id, :scope => :candy_id

user.primary_candy.candy会返回相关的糖果。 user.candy将返回所有糖果的集合。

您可以在糖果模型中创建一个方法来检查primary_candy和.find也只记录该记录。

def p_candy(user)
  p_candy_id = Primary_candy.find_by_user_id(user.id).candy_id
  p_candy = Candy.find(p_candy_id)
  return p_candy
end

它还使您可以轻松查看不同主要糖果的相对受欢迎程度。 只是,GL。

答案 2 :(得分:0)

在我看来,人们经常通过为不存在的一对多关系创建一个不必要的链接表来使这种情况变得不必要地复杂化。 MyFavoriteColor是“我”(“最喜欢的”)的monadic属性所以我会这样做:

      Tables:
         PEOPLE
         COLORS

和People.FavoriteColor列必须包含COLORS表中的键:

       ALTER TABLE PEOPLE ADD CONSTRAINT FK_PEOPLE_COLOR
       foreign key(FavoriteColor) REFERENCES COLOR(id)

现在,假设你有一家服装店,你想跟踪一件商品的颜色(红色衬衫,蓝色衬衫,绿色衬衫),为什么你需要一对多的关系联动表:

       Tables:
           APPAREL
           COLORS
           APPARELCOLORS

       T-Shirt is an item in the APPAREL table
       Red, green, blue, white, etc are items in the COLORS table


        And then you'd have this in the APPARELCOLORS linkage table:


             Apparel | Color
             t-shirt | red
             t-shirt | blue
             hat   | white
             hat | black
             hat | yellow

“最喜欢的”规则 - 也就是说,只有与实体相关的几个值中的一个可以将“最喜欢的”列设置为True--无法强制执行。您应始终寻求一种以声明方式强制执行规则的方法。如果可以的话,你知道你有一个好的设计。