【问题标题】:How to efficiently design database of multi list application如何高效设计多列表应用的数据库
【发布时间】:2013-12-30 05:54:11
【问题描述】:

对于冗长的内容,我深表歉意,但我正在尝试为我正在开发的示例应用程序设计一个设计。下面我解释了一个示例案例、示例案例的Desired Queries,以及我的DB设计

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

示例:

场景:

  • John 创建了两个主题 Math 和 Science
    • 在数学中添加微积分和代数项,在科学中添加物理项
  • James 创建了两个主题 Math 和 Music
    • 在数学中添加预计算项,在音乐中添加摇滚
  • Lisa 创建了两个主题 Math 和 CompSci
    • 将线性代数项添加到数学,将 Java 项添加到 CompSci
  • John 与 James 分享他在数学主题中的项目演算
    • James 接受该项目
    • James 与 Lisa 共享同一个项目
    • Lisa 接受物品

所需查询:

约翰

  • 主题列表和每个项目的数量将显示:
    • 数学 - 2,科学 - 1
  • Math 中的项目列表将显示每个项目的共享计数和共享者
    • 微积分 - 2 - 你,代数 - 0 - NULL

詹姆斯

  • 主题列表和每个项目的数量将显示:
    • 数学 - 2,音乐 - 1
  • Math 中的项目列表将显示每个项目的共享计数和共享者
    • 微积分 - 2 - James,Pre-Calc - 0 - NULL

丽莎

  • 主题列表和每个项目的数量将显示:
    • 数学 - 2, CompSci - 1
  • Math 中的项目列表将显示每个项目的共享计数和共享者
    • 微积分 - 2 - 詹姆斯,线性代数 - 0 - NULL

当前的数据库设计

用户:

ID     username
----   ----------
1        John
2        James
3        Lisa

主题

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

物品

ID     Item_Name        Topic_Id
---   ----------       ----------
1      Calculus           1
2      Algebra            1
3      Physics            2
4      Pre-Calc           3
5      Rock               4
6      Linear Algebra     5
7      Java               6

分享

ID   Item_Id    Sent_user_id    Accepted_user_id
---  -------   -------------    -----------------
1     1          1                 2
1     1          1                 3

对我来说,上述数据库设计很有意义,但我无法获得我想要的查询结果。我不确定我的查询是否可以改进,或者我应该更改我的设计以更适合我想要的查询

查询 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

上述查询为John

返回以下内容
topic_name      item_count
-----------    ------------
Math              3
Science           1

但它应该为John

返回以下内容
topic_name      item_count
-----------    ------------
Math              2
Science           1

应该为James返回关注

topic_name      item_count
-----------    ------------
Math              2
Music             1

应该为Lisa

返回关注
topic_name      item_count
-----------    ------------
Math                2
CompSci             1

请注意,item_count 对于 James 应该是 2。我相信上述查询适用于其他用户。

查询 2:通过 USERID 获取项目及其共享计数以及最初共享它们的人

   --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 对于我期望的数学:

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

Lisa for Math:

Item Name         Share Count     Orig Shared By
---------         -----------    -----------------
Calculus              2              James
Linear Algebra        0              NULL

更新

根据 cmets 和答案,我稍微更改了 DB Schema。我制作了一个单独的连接表,用于建立用户-主题-项目之间的关系。

请看这个 sql fiddle:http://sqlfiddle.com/#!4/15211/15

但我仍然需要查询方面的帮助。我觉得我很接近了。

【问题讨论】:

  • 为什么你觉得你现在的设计效率不高?你希望得到什么?只是好奇。
  • 我希望得到两个查询的答案,这将回答每个用户的问题。我认为当前的设计没有达到标准,因为我很难对我的两个问题提出疑问。 1)获取每个用户的主题,包括每个用户的项目数(包括共享数),例如James Sub-bullet one 2)获取每个项目的共享计数,包括共享计数,例如詹姆斯子子弹二
  • 我已经编辑了我想要提供更多洞察力的查询。
  • 我会在某些方面改变你的设计。 1 - 我会用独特的主题制作主题表; 2-我会为它做同样的事情。 Itens 将是独一无二的,我将在 Itens 和 Topics 之间有一个 n-m 表(Cauculus 可以是数学或科学,因为物理是一个项目)。最后我会在 itemTopics 和 Users 之间有一个 N-M 表。

标签: mysql sql database-design relational-database database-schema


【解决方案1】:

新设计为:

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

SELECT DISTINCT u.username, T.Topic_Name ,   
(
  (SELECT count(*) FROM UserTopicItem
   WHERE UserTopicItem.topic_id= t.id
   AND   UserTopicItem.user_id = UTI.user_id)
  +
  (SELECT count(*) FROM Shares
   WHERE Shares.Topic_id = UTI.Topic_id
   AND Shares.Accepted_user_id = u.id)
) AS item_count
FROM UserTopicItem UTI
INNER JOIN USERS u ON u.id = UTI.user_id
INNER JOIN topics T ON T.id = UTI.Topic_Id
-- where u.id = ?
ORDER BY u.username;

查询 2:获取项目及其共享计数以及最初共享的人 他们按用户 ID

SELECT i.item_name, t.topic_name
,(SELECT count(*) FROM shares 
  WHERE shares.Item_Id = i.id
  and shares.user_id   = u.id
  and shares.topic_id  = t.id
 ) AS shared_count
,decode((SELECT count(*) FROM shares 
  WHERE shares.Item_Id = i.id
  and shares.user_id   = u.id
  and shares.topic_id  = t.id
 ),0,'Null',u.username) AS Orig_Shared_By 
FROM UserTopicItem UTI 
INNER JOIN Items i  ON UTI.item_id = i.id
INNER JOIN topics t ON UTI.topic_id = t.id
INNER JOIN Users u  ON UTI.user_id = u.id
-- where u.id = ?
order by u.id,shared_count desc;

sqlfiddle demo

【讨论】:

  • 谢谢。但是查询一个与我遇到的问题相同。对于 James,Math 的计数应该是 2,因为他有 Pre-calc 和 Calculus(由另一个用户分享给他)。我已经用每个场景所需的查询结果更新了问题,请看一下。我认为对于查询一,共享表也需要查询。
  • 这个架构还是有问题的。您真的不应该有一个主题表,其中包含每个用户重复的“数学”等条目。另外,您的项目可以属于多个主题吗?例如,“物理”可以是数学和科学。
  • @Patrick 我更新了答案,ChrisLava 的评论也是完全正确的。
  • @ChrisLava 我没有想到该项目属于多个主题。但这是有道理的。在未来,我希望能够灵活地允许这样做。当我开始编写联合查询等时,我知道架构不在应有的位置。有关架构的任何指导?
  • @MohsenHeydari 谢谢!我现在正在查看结果。我将使用您提供的提示创建新的 sqlfiddle
【解决方案2】:

我认为你的设计应该更加规范化一点:

用户:

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 框架以获取实现思路。)

就个人而言,我建议使用完全独立的表来跟踪所有权,而不是在 SubTopics 中使用 owner_id:您永远不知道将来何时可能需要共享节点所有权。

不过,为了简化您的示例,我们将添加一个 owner_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

SubTopic_Access:

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

您的第二个查询似乎是在计算可以访问用户有权访问的子主题的 other 用户的数量。 (我同样认为 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

(在上面,将始终填充 Owner_Id 字段。如果需要,您可以在其上使用 NullIf 来取消所需的值。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-03
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 2019-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多