2

我遇到了这个问题:

  • 用户有很多糖果(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      |

解决方案 1 的注释

  • 我在 Candies table 中得到了与 Candy 相关的所有内容
  • 如果我检查是否有任何 Candy 是主要的,则查询速度更快(不必加入)
  • 如果用户有 100 个糖果,这样他就有 99 个是的,只有 1 个有意义的=> 99 个不必要的列记录

解决方案 2 的注释

  • 这样我将只有一个列记录(保存 99 个其他)
  • 如果我想检查 Candy 是否是主要的,我需要加入客户表
  • 假设我介绍了更多带有与用户相关的主要标志的 1:M 表:冰淇淋、巧克力、垃圾食品……这样我将在用户中再添加 4 个列

这是更多的 SQL 问题,但我在项目中使用 Rails 3.*,因此欢迎来自该领域的最佳实践意见

4

3 回答 3

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 表中添加列,而且,由于用户可能有也可能没有最喜欢的糖果,您可以通过这种方式避免 users 表中的空字段.

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

因此,从关系数据库的角度来看,我的简短回答是:我不会有标志,我会将主要糖果放在单独的模型中。

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

我使用上述模型代码制作了一个示例 rails 项目,可在此处获得。

于 2012-07-20T13:27:33.193 回答
0

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

      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——不能以声明方式强制执行。您应该始终寻求以声明方式执行规则的方法。当你可以的时候,你知道你有一个好的设计。

于 2012-07-20T16:59:53.113 回答
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 将返回所有糖果的集合。

您可以在 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。

于 2012-07-20T13:28:35.277 回答