Mysql 如何高效地设计多列表应用程序的数据库

Mysql 如何高效地设计多列表应用程序的数据库,mysql,sql,database-design,relational-database,database-schema,Mysql,Sql,Database Design,Relational Database,Database Schema,我很抱歉说得太详细了,但我正在尝试为我正在开发的示例应用程序进行设计。下面我解释了一个示例案例、示例案例的所需查询,以及我的数据库设计 我正在寻找关于如何改进当前设计的建议,以使我能够回答示例部分中的问题 示例: 情景: 约翰创造了两个主题:数学和科学 将微积分和代数添加到数学中,将物理添加到科学中 詹姆斯创作了两个主题:数学和音乐 在数学中添加预科,在音乐中添加摇滚 Lisa创建了两个主题:数学和计算机科学 将项目线性代数添加到数学中,将Java添加到CompSci中 John与

我很抱歉说得太详细了,但我正在尝试为我正在开发的示例应用程序进行设计。下面我解释了一个示例案例、示例案例的所需查询,以及我的数据库设计

我正在寻找关于如何改进当前设计的建议,以使我能够回答示例部分中的问题

示例:

情景:

  • 约翰创造了两个主题:数学和科学
    • 将微积分和代数添加到数学中,将物理添加到科学中
  • 詹姆斯创作了两个主题:数学和音乐
    • 在数学中添加预科,在音乐中添加摇滚
  • Lisa创建了两个主题:数学和计算机科学
    • 将项目线性代数添加到数学中,将Java添加到CompSci中
  • John与James分享了主题数学中的微积分项目
    • 詹姆斯接受了这个项目
    • 詹姆斯和丽莎共用同一件物品
    • 丽莎接受了这个项目
所需查询:

约翰

  • 主题列表和每个主题中的项目数将显示:
    • 数学-2,科学-1
  • 数学中的项目列表将显示每个项目的共享计数以及共享者
    • 微积分-2-你,代数-0-零
詹姆斯

  • 主题列表和每个主题中的项目数将显示:
    • 数学-2,音乐-1
  • 数学中的项目列表将显示每个项目的共享计数以及共享者
    • 微积分-2-詹姆斯,预计算-0-空
丽莎

  • 主题列表和每个主题中的项目数将显示:
    • 数学-2,计算机科学-1
  • 数学中的项目列表将显示每个项目的共享计数以及共享者
    • 微积分-2-詹姆斯,线性代数-0-零
当前数据库设计

用户:

话题

ID      Topic_Name      User_id
---     ---------       --------
1        Math           1
2        Science        1
3        Math           2
4        Music          2
5        Math           3
6        CompSci        3
项目

分享

对我来说,上面的DB设计是有意义的,但是我在获得我想要的查询结果时遇到了困难。我不确定我的查询是否可以改进,或者我应该改变我的设计以更好地适应我想要的查询

查询1:按用户ID获取主题及其项目数

-- This only works for certain cases
SELECT t.topic_name, count(topic_name) as item_count
FROM Topics t
INNER JOIN Items i on i.topic_id = t.id
INNER JOIN User on u.id = t.user_id
INNER JOIN Share s on s.item_id = i.id
WHERE u.id = 1
GROUP BY t.topic_name
   --I'm not sure how to start on this. I've tried union with Items table and Share table
   -- but that also works only for few users and not for all cases. 
上面的查询为John返回以下结果

但是它应该为John返回以下内容

应该为詹姆斯返回以下位置

应该为Lisa返回以下内容

请注意,詹姆斯的
项目计数应为2。我相信上面的查询对于其他用户来说效果很好

查询2:获取项目及其共享计数,以及最初按USERID共享的用户

-- This only works for certain cases
SELECT t.topic_name, count(topic_name) as item_count
FROM Topics t
INNER JOIN Items i on i.topic_id = t.id
INNER JOIN User on u.id = t.user_id
INNER JOIN Share s on s.item_id = i.id
WHERE u.id = 1
GROUP BY t.topic_name
   --I'm not sure how to start on this. I've tried union with Items table and Share table
   -- but that also works only for few users and not for all cases. 
对于约翰的数学,我希望:

Item Name    Share Count     Orig Shared By
---------    -----------    -----------------
Calculus       2              You
Algebra        0              NULL
对于詹姆斯的数学:

Item Name    Share Count     Orig Shared By
---------    -----------    -----------------
Calculus        2              James
Pre-Calc        0              NULL
Item Name         Share Count     Orig Shared By
---------         -----------    -----------------
Calculus              2              James
Linear Algebra        0              NULL
对于丽莎的数学:

Item Name    Share Count     Orig Shared By
---------    -----------    -----------------
Calculus        2              James
Pre-Calc        0              NULL
Item Name         Share Count     Orig Shared By
---------         -----------    -----------------
Calculus              2              James
Linear Algebra        0              NULL
更新

根据评论和回答,我稍微更改了DB模式。我制作了一个单独的联接表,用于建立用户主题和项目之间的关系

请查看此sql小提琴:

但我仍然需要有关查询的帮助。我觉得我很接近

新设计为:

查询1:按用户ID获取主题及其项目数

-- This only works for certain cases
SELECT t.topic_name, count(topic_name) as item_count
FROM Topics t
INNER JOIN Items i on i.topic_id = t.id
INNER JOIN User on u.id = t.user_id
INNER JOIN Share s on s.item_id = i.id
WHERE u.id = 1
GROUP BY t.topic_name
   --I'm not sure how to start on this. I've tried union with Items table and Share table
   -- but that also works only for few users and not for all cases. 
查询2:获取项目及其共享计数以及最初共享的用户 通过用户ID进行编辑

-- This only works for certain cases
SELECT t.topic_name, count(topic_name) as item_count
FROM Topics t
INNER JOIN Items i on i.topic_id = t.id
INNER JOIN User on u.id = t.user_id
INNER JOIN Share s on s.item_id = i.id
WHERE u.id = 1
GROUP BY t.topic_name
   --I'm not sure how to start on this. I've tried union with Items table and Share table
   -- but that also works only for few users and not for all cases. 

我认为您的设计应该更加规范化:

用户:

Id     username
----   ----------
1        John
2        James
3        Lisa
主题:

Id      Topic_Name
---     ---------
1        Math
2        Science
3        Music
4        CompSci
分主题:

Id    SubTopic_Name    Topic_Id
---   ----------       --------
1      Calculus           1
2      Algebra            1
3      Physics            2
4      Pre-Calc           1
5      Rock               3
6      Linear Algebra     1
7      Java               4
这样,您就可以从用户和主题/子主题作为正交集开始—如果您愿意,完全不相关。从这里开始,根据您的用例添加新的字段和/或关系来连接它们

您似乎需要跟踪权限。有许多方法可以存储与权限相关的数据。要获得灵感,请特别关注ACL(访问控制列表)和RBAC(基于角色的访问控制)。(如果您使用的是PHP,请查看Symfony或Yii框架的实现思想。)

就我个人而言,我建议使用一个完全独立的表来跟踪所有权,而不是在子主题中使用所有者id:你永远不知道将来什么时候可能需要节点的共享所有权

不过,为了简化您的示例,我们将添加一个owner\u id字段,并坚持使用一个过于简单的权限表:

子主题变成:

Id    SubTopic_Name    Topic_Id    Owner_Id
---   -------------    --------    --------
1      Calculus           1           1
2      Algebra            1           1
3      Physics            2           1
4      Pre-Calc           1           2
5      Rock               3           2
6      Linear Algebra     1           3
7      Java               4           3
次主题访问:

User_Id  SubTopic_Id
--—----  --—------—-
   1          1            # John created Calculus
   1          2     
   1          3     
   2          4            # James created Pre-Calc
   2          5     
   3          6            # Lisa created Linear Algebra
   3          7     
   2          1            # James accepted Calculus
   3          1            # Lisa accepted Calculus
(将包括所有者在内的所有有权访问的用户放在后一个表中的好处是简化了以后的查询。)

如果您需要跟踪电子邮件之外的邀请,请将邀请放入不同的“邀请”表中,或者在上表中添加一个字段(如果您不介意将
Is_Active=1
子句到处移动)。(Fwiw,除非您使用的是视图,否则后者很乏味,对于索引来说也不是很好。)

您的第一个查询似乎是计算用户在每个主题中可以访问的子主题的数量。一个起点可以是:

select s.Topic_Id, count(s.Id)
from SubTopics s
join SubTopic_Access a
  on a.SubTopic_Id = s.Id
where a.User_id = ?
group by s.Topic_Id
您的第二个查询似乎是计算可以访问用户有权访问的子主题的其他用户的数量。(我同样认为Orig Shared By每次都应该显示John。)起点计数如下:

select s.Id, count(o.User_Id), s.Owner_id
from SubTopics s
join SubTopic_Access a
  on a.SubTopic_Id = s.ID
left join SubTopic_Access o
  on o.SubTopic_Id = s.ID
 and o.User_id <> a.User_Id
where s.Topic_Id = ?
  and a.User_id = ?
group by s.Id, s.Owner_id
选择s.Id、计数(o.User\u Id)、s.Owner\u Id
从副主题
加入子主题访问a
关于a.SubTopic\u Id=s.Id
左连接子主题\u访问o
关于o.SubTopic_Id=s.Id
和o.User\u id a.User\u id
其中s.Topic_Id=?
和a.User_id=?
按s.Id、s.Owner\U Id分组

(在上述情况下,将始终填充Owner_Id字段。如果需要,您可以在其上使用NullIf将值置零。)

为什么您认为当前的设计效率低下?你希望得到什么?只是好奇。我想得到两个问题的答案,这两个问题将回答每个用户的问题。我认为目前的设计不符合标准,因为我很难对我的问题提出质疑。1) 获取每个用户的主题,包括每个主题中的项目数量(包括共享数量),例如James Sub bullet one 2)获取
select s.Id, count(o.User_Id), s.Owner_id
from SubTopics s
join SubTopic_Access a
  on a.SubTopic_Id = s.ID
left join SubTopic_Access o
  on o.SubTopic_Id = s.ID
 and o.User_id <> a.User_Id
where s.Topic_Id = ?
  and a.User_id = ?
group by s.Id, s.Owner_id