包含3个表的1个数据库:用户 - 照片 - 投票
- 用户可以拥有许多照片
- 一张照片可以有很多票
- 用户可以对许多照片进行投票
- 投票记录:
。结果为int(-1 / disliked,0 / neutral,1 / likes)
。投票的用户的身份。
这就是我所拥有的(所有FK在删除和更新时都是级联的):
http://grab.by/iZYE
(sid =代理人ID)
我的问题是:这似乎不对,我已经看了2天了,不能自信地继续前进。我怎样才能优化这个或者我完全错了?
答案 0 :(得分:1)
我看到的第一件事是你在表上有重复的唯一ID。您不需要sid
列;只需使用user_id
,photo_id
和photo_user_id
(可能会将此值重命名为vote_id
)。这些ID列也应该是INT
类型,绝对不是VARCHAR
。您可能不需要photo
上的投票总栏数;您可以运行查询以在需要时获取总数,而不必担心保持两个表同步。
假设您只允许每张照片在每张照片上投一票,可以修改其结构,因此唯一的列是user_id
,photo_id
和vote_result
。然后,您可以将主键设为(user_id
,photo_id
)上的复合索引。但是,由于你使用的是外键,这使得这个表格更加复杂。
答案 1 :(得分:1)
MySQL / InnoDB表始终是聚类的(更多关于聚类here和here)。
由于主键也充当集群键 1 ,因此使用代理主键意味着您对表进行物理排序,使其对客户端应用程序没有用处,并且不能用于查询。
此外,群集表中的二级索引可能比基于堆的表“更胖”,并且可能需要双重查找。
由于这些原因,您需要避免使用代理并使用更多“自然”键,类似于:
(表{USER_ID, PICTURE_NO}
中的VOTE
引用PICTURE
中相同名称的字段。VOTE.VOTER_ID
引用USER.USER_ID
。使用*_ID
的整数如果可以,请输入1}}和*_NO
字段。)
此物理模型将实现极为高效的查询:
PICTURE
主要/群集索引上的简单范围扫描)。VOTE
主要/群集索引进行简单范围扫描)。根据具体情况,这实际上可能足够快,因此您无需在PICTURE
中缓存总和。如果您需要给定用户的投票,请将VOTE
PK更改为:{VOTER_ID, USER_ID, PICTURE_NO}
。如果您同时需要(图片投票和用户投票),请保留现有PK,但在{VOTER_ID, USER_ID, PICTURE_NO, VOTE_VALUE}
上创建covering索引。
1 在InnoDB中。有DBMS(例如MS SQL Server),其中群集密钥可能与主要密钥不同。