我希望建立“{3}}或FourSquare等”登记“服务。
如何设计合适的数据库架构来存储签到?
例如,假设我正在开发“CheeseSquare”来帮助人们跟踪他们尝试过的美味奶酪。
可以签入的项目表非常简单,看起来像
+----+---------+---------+-------------+--------+
| ID | Name | Country | Style | Colour |
+----+---------+---------+-------------+--------+
| 1 | Brie | France | Soft | White |
| 2 | Cheddar | UK | Traditional | Yellow |
+----+---------+---------+-------------+--------+
我也会为用户提供一个表格,比如说
+-----+------+---------------+----------------+
| ID | Name | Twitter Token | Facebook Token |
+-----+------+---------------+----------------+
| 345 | Anne | qwerty | poiuyt |
| 678 | Bob | asdfg | mnbvc |
+-----+------+---------------+----------------+
用户签入特定奶酪的最佳记录方式是什么?
例如,我想记录安妮已经办理了多少法国奶酪。鲍勃已经检查了哪些奶酪等。如果Cersei吃过卡门培尔奶酪超过5次等。
我最好把这些信息放在用户的桌子上吗? E.g。
+-----+------+------+--------+------+------+---------+---------+
| ID | Name | Blue | Yellow | Soft | Brie | Cheddar | Stilton |
+-----+------+------+--------+------+------+---------+---------+
| 345 | Anne | 1 | 0 | 2 | 1 | 0 | 5 |
| 678 | Bob | 3 | 1 | 1 | 1 | 1 | 2 |
+-----+------+------+--------+------+------+---------+---------+
看起来相当笨拙且难以维持。那么我应该有单独的录音登记表吗?
答案 0 :(得分:1)
不,不要把它放到users
表中。该信息最好存储在连接表中,表示用户和奶酪之间的多对多关系。
连接表(我们称之为cheeses_users
)必须至少有两列(user_ID, cheese_ID
),但第三列(时间戳)也是有用的。如果将timestamp列默认为CURRENT_TIMESTAMP
,则只需将user_ID, cheese_ID
插入表中即可记录签入。
cheeses (ID) ⇒ (cheese_ID) cheeses_users (user_ID) ⇐ users (ID)
创建为:
CREATE TABLE cheeses_users
cheese_ID INT NOT NULL,
user_ID INT NOT NULL,
-- timestamp defaults to current time
checkin_time DATETIME DEFAULT CURRENT_TIMESTAMP,
-- (add any other column *specific to* this checkin (user+cheese+time))
--The primary key is the combination of all 3
-- It becomes impossible for the same user to log the same cheese
-- at the same second in time...
PRIMARY KEY (cheese_ID, user_ID, checkin_time),
-- FOREIGN KEYs to your other tables
FOREIGN KEY (cheese_ID) REFERENCES cheeses (ID),
FOREIGN KEY (user_ID) REFERENCES users (ID),
) ENGINE=InnoDB; -- InnoDB is necessary for the FK's to be honored and useful
登录Bob&的签到Cheddar,插入:
INSERT INTO cheeses_users (cheese_ID, user_ID) VALUES (2, 678);
要查询它们,请通过此表加入。例如,要查看每个用户的每种奶酪类型的数量,您可以使用:
SELECT
u.Name AS username,
c.Name AS cheesename,
COUNT(*) AS num_checkins
FROM
users u
JOIN cheeses_users cu ON u.ID = cu.user_ID
JOIN cheeses c ON cu.cheese_ID = c.ID
GROUP BY
u.Name,
c.Name
要获取给定用户的5个最新签到,例如:
SELECT
c.Name AS cheesename,
cu.checkin_time
FROM
cheeses_users cu
JOIN cheeses c ON cu.cheese_ID = c.ID
WHERE
-- Limit to Anne's checkins...
cu.user_ID = 345
ORDER BY checkin_time DESC
LIMIT 5
答案 1 :(得分:1)
让我们更清楚地定义,所以你可以告诉我我是不是错了:
如果是这种情况,那么要存储完全规范化的数据,并且能够检索该数据的历史记录,您需要一个链接两个现有表的第三个关系表。
+-----+------------+---------------------+
| uid | cheese_id | timestamp |
+----+-------------+---------------------+
| 345 | 1 | 2014-05-04 19:04:38 |
| 345 | 2 | 2014-05-08 19:04:38 |
| 678 | 1 | 2014-05-09 19:04:38 |
+-----+------------+---------------------+
等。您可以添加额外的列以对应于奶酪数据,但严格来说您不需要。
通过将所有这些放在第三个表中,您可以提高性能和灵活性。您始终可以使用聚合查询重建对您提出的用户表的添加。
如果你真的认为你不需要时间戳,那么你将用基本相当于COUNT(*)字段的方式替换它们:
+-----+------------+--------------+
| uid | cheese_id | num_checkins |
+----+-------------+--------------+
| 345 | 1 | 15 |
| 345 | 2 | 3 |
| 678 | 1 | 8 |
+-----+------------+--------------+
这会大大减少你的加入表的大小,虽然显然没有“纸质记录”,如果你需要重建你的数据(并且可能对用户说“哦,是的,我们忘记记录你的签到在这样的日期。“)
答案 2 :(得分:0)
实体'User'和'Cheese'具有多对多关系。用户可以检查多个奶酪,奶酪可以有多个人检查。
在关系数据库中设计它的唯一正确方法是将其存储到单独的表中。例如,将其存储到用户表中的原因有很多,这是一个非常糟糕的主意。阅读有关规范化数据库的更多信息。
你的表应该是这样的:
CheckIns(CheeseId, UserId, (etc...))
其他有用的列可能包括日期或评级,或者您想要存储的关于用户和奶酪之间特定关系的任何内容。