【问题标题】:A simplified version of Twitter. Understanding many-to-many relationships between tables in the databaseTwitter 的简化版本。了解数据库中表之间的多对多关系
【发布时间】:2017-01-22 12:16:31
【问题描述】:

我正在阅读this article about a Twitter-like application. 将存储推文、用户、喜欢等的存储类型是关系数据库。数据库方案描述here绘制here.

作为一名 Android 开发人员,我使用 SQLite 编写了我的示例。这就是我的编码方式:

create table users (_id integer primary key, username text unique, first_name text, last_name text);
create table tweets (_id integer primary key, content text, created_at integer, user_id integer, foreign key(user_id) references users(_id));
create table connections (_id integer primary key, follower_id integer, followee_id integer, created_at integer, foreign key(follower_id) references users(_id), foreign key (followee_id) references users(_id));
create table favorites (_id integer primary key, user_id integer, tweet_id integer, foreign key (user_id) references users(_id), foreign key (tweet_id) references tweets(_id));

现在让我们插入一些数据。

用户:

insert into users values (1, 'user1', 'Lorem', 'Ipsum');
insert into users values (2, 'user2', 'Dolor', 'Sit');
insert into users values (3, 'user3', 'Foo', 'Bar');
insert into users values (4, 'user4', 'Qwerty', 'Trewq');

一些推文:

insert into tweets values(10, '1 Tweet from user1', 1100, 1);
insert into tweets values(11, '2 Tweet from user1', 1101, 1);
insert into tweets values(12, '3 Tweet from user1', 1102, 1);
insert into tweets values(13, '4 Tweet from user1', 1103, 1);

insert into tweets values(14, '1 Tweet from user2', 1103, 2);
insert into tweets values(15, '2 Tweet from user2', 1103, 2);

insert into tweets values(16, '1 Tweet from user3', 1103, 3);
insert into tweets values(17, '2 Tweet from user3', 1103, 3);

insert into tweets values(18, '1 Tweet from user4', 1107, 4);

最喜欢的(和喜欢的一样):

insert into favorites values(1, 2, 11);
insert into favorites values(2, 3, 13);
insert into favorites values(3, 4, 15);

有一个关于数据库方案的问题:

您认为您可以支持我们的数据库设计能力吗 为给定用户显示一个页面,其中包含他们最新的推文 至少收藏过一次?

是的,这就是为什么要查询:

sqlite> select favorites._id, tweets._id as tweet_row_id, tweets.content from favorites join tweets on tweets.user_id=1 and tweets._id = favorites.tweet_id order by tweets._id desc limit 1;
_id         tweet_row_id  content
----------  ------------  ------------------
2           13            4 Tweet from user1

解释:

左边的数据集是表favorites。正确的数据集是表 tweets。我加入了这两个数据集。然后 tweets.user_id=1 and tweets._id = favorites.tweet_id 对结果数据集的每一行进行评估,作为布尔表达式。如果结果为真,则包含该行。 order by tweets._id desc 用于获取最新的推文(tweets._id 越大,推文越新)。 limit 用于限制行数。如果用户多年来一直使用我们的类似 Twitter 的应用程序,我们将显示最新的 10 或 20 条推文。


我的问题。

  1. 我的数据库方案有什么问题吗?为简单起见,我省略了 not nullunique 和其他列约束。
  2. Here原作者说:

第一个关系是通过将用户 ID 粘贴到每条推文来解决的。 这是可能的,因为每条推文都是由一个用户创建的。 当涉及到关注用户和 收藏推文。那里的关系是多对多的。

“第一个关系”是用户-推文。

这里为什么需要多对多?在我的方案中,我只使用一对多。

更新 1

【问题讨论】:

    标签: sql database-design


    【解决方案1】:

    很快我placed an answer here,OP - 就像你在这个问题中一样 - 不确定1:nn:m

    我假设您的最后一句话是您的实际问题:

    这里为什么需要多对多?在我的方案中,我只使用一对多

    用户与推文的关系是1:n...

    在对象中思考

    • 用户(ID、姓名、...)
    • tweet(id、作者(用户上的 FK)、日期时间、内容...)

    like 是一个对象,它有自己的具体细节:

    • 点赞(id、userid、tweetid、datetime、...)

    为此,您需要一个映射表(您称之为收藏夹

    用户与此映射之间存在1:n-关系,而推文与此映射之间存在1:n-关系。
    这两个1:n-关系共同构成m:n-关系.

    现在每条推文都可以被很多用户点赞,每个用户可以点赞很多条推文,但一个用户应该(可能)不应该两次喜欢同一条推文(唯一键,甚至是两列 PK?)。您可能会引入CHECK 约束以确保喜欢的用户和作者的用户ID 不同(不喜欢您自己的推文)。

    附带说明:

    我的数据库方案有什么问题

    你不应该在没有命名的情况下创建约束

    CREATE TABLE Dummy
    (
     ID INT IDENTITY CONSTRAINT PK_Dummy PRIMARY KEY
    ,UserID INT NOT NULL CONSTRAINT FK_Dummy_UserID FOREIGN KEY REFERENCES User(id)
    ,...
    )
    

    如果这个数据库曾经安装在不同的系统上,它们会得到不同的(随机的)名称,未来的升级脚本会让你陷入最痛苦的境地......

    更新:附注示例

    在你的评论中你问,最后一句话是关于什么的......试试这个

    CREATE DATABASE testDB;
    GO
    USE testDB;
    GO
    CREATE TABLE testTbl1(ID INT IDENTITY PRIMARY KEY,SomeValue INT UNIQUE);
    CREATE TABLE testTbl2(ID INT IDENTITY PRIMARY KEY,FKtoTbl1 INT NOT NULL FOREIGN KEY REFERENCES testTbl1(ID));
    GO
    CREATE TABLE testTbl3(ID INT IDENTITY CONSTRAINT PK_3 PRIMARY KEY,SomeValue INT CONSTRAINT UQ_3_SomeValue UNIQUE);
    CREATE TABLE testTbl4(ID INT IDENTITY CONSTRAINT PK_4 PRIMARY KEY,FKtoTbl3 INT NOT NULL CONSTRAINT FK_4_FKtoTbl3 FOREIGN KEY REFERENCES testTbl3(ID));
    
    GO
    SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS;
    GO
    USE master;
    GO
    DROP DATABASE testDB;
    GO
    

    结果中的 On 列如下所示:

    CONSTRAINT_NAME
    ------------------------------
    PK__testTbl1__3214EC27ABEA2C0C
    UQ__testTbl1__0E5C381C04C8AF66
    PK__testTbl2__3214EC272784631C
    FK__testTbl2__FKtoTb__1367E606
    PK_3
    UQ_3_SomeValue
    PK_4
    FK_4_FKtoTbl3
    

    如果此脚本运行两次,给定名称将保持为定义的名称。其他名称将获得一个随机名称,例如PK__testTbl1__3214EC27ABEA2C0C。现在想象一下,您需要为必须删除或修改一个约束的多个已安装系统创建升级脚本。如果你不知道它的名字,你会怎么做?

    【讨论】:

    • If this database was ever installed on different systems, they'll get different (random) names and future upgrade scripts will get you in deepest pain...这部分没看懂
    • These two 1:n-relations form the m:n-relation together 我不确定我是否理解这部分内容。当一个作者写很多书,而同一本书可以由几个作者写时,两个实体(由表表示)之间存在多对多关系:作者和书籍。但是对于我的例子,哪些实体具有多对多关系?
    • @MaksimDmitriev 要为m:n-relation 建模,您需要在中间放置一个表格。在您的示例中,这是AuthorBook,带有一个FK to Author 和一个FK to Book。简单案例 1 本书 1 位作者是一排。 1 个作者很多书是每本书的一行,所有作者都具有相同的作者,许多作者 1 本书与以前一样,但反之亦然,m:n 是两者的混合......
    • “我的例子”是指问题中的 Twitter 例子
    • @MaksimDmitriev 没那么复杂 :-) ... 一张桌子推文,一位桌子用户。一个用户可以发布多条推文,每条推文只有一个作者。这是1:n 相关的。现在很多用户可以喜欢很多推文。在这里,您需要一个映射表,您可以在其中看到多个组合。询问哪些用户喜欢某条给定推文?或询问给定用户喜欢哪些推文?这需要m:n-relation...
    猜你喜欢
    • 1970-01-01
    • 2011-11-04
    • 2021-06-13
    • 2011-12-27
    • 2014-10-03
    • 2018-07-16
    • 2016-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多