假设我有一个包含 3 个表的 MySQL 数据库:
表 1:人员,具有 1 列 ID (int)
表 2:时事通讯,具有 1 列 ID (int)
表 3:订阅,包含列 Person_ID (int)、Newsletter_ID (int)、Subscribed (bool)、Updated (Datetime)
Subscriptions.Person_ID 指向一个 Person,而 Subscription.Newsletter_ID 指向一个 Newsletter。因此,每个人可能同时订阅了 0 种或更多杂志。订阅表还将存储每个人订阅每个时事通讯的完整历史记录。如果特定的 Person_ID-Newsletter_ID 对在 Subscriptions 表中没有一行,则它等同于该对的订阅状态为“false”。
这是一个示例数据集
Persons
ID
1
2
3
Newsletters
ID
1
2
3
Subscriptions
Person_ID Newsletter_ID Subscribed Updated
2 1 true 2010-05-01
3 1 true 2010-05-01
3 2 true 2010-05-10
3 1 false 2010-05-15
因此,截至 2010-05-16,人员 1 没有订阅,人员 2 订阅了时事通讯 1,人员 3 订阅了时事通讯 2。人员 3 有一段时间订阅了时事通讯 1,但现在不是了。
我正在尝试进行 2 种查询。
查询显示截至查询时每个人的活跃订阅(我们可以假设更新永远不会在未来发生——因此,这意味着为每个 Person_ID-Newsletter_ID 返回具有最新“更新”值的记录对,只要 Subscribed 为真(如果 Person_ID-Newsletter_ID 对的最新记录的订阅状态为 false,那么我不希望返回该记录)。
返回特定时事通讯的所有事件订阅的查询 - 与 1. 中关于“已订阅”列中包含“false”的记录的条件相同。
我使用 SQL/数据库的频率不够高,无法判断这种设计是否良好,或者所需的 SQL 查询在订阅表中有 1M 记录的数据库上是否会很慢。
我在 Visual Studio 2010 中使用可视化查询生成器工具,但我什至无法获得查询以返回每个 Person_ID-Newsletter_ID 对的最新更新记录。
是否有可能提出不涉及使用子查询的 SQL 查询(可能是因为它们在处理更大的数据集时会变得太慢)?如果不是,那么有一个单独的 Subscriptions_History 表是否是更好的设计,并且每次将 Person_ID-Newsletter-ID 对的订阅状态添加到订阅时,该对的任何现有记录都将移动到 Subscriptions_History(这样订阅表只包含任何 Person_ID-Newsletter_ID 对的最新状态更新)?
我在 Windows 上使用 .net,那么使用 Linq 执行此类查询会更容易(或相同,或更难)吗? Entity Framework ?
编辑:如果我使用此查询,会发生以下情况:
SELECT Person_ID, Newsletter_ID, Allocation, Updated, MAX(Updated) AS Expr1
FROM subscriptions
GROUP BY Person_ID, Newsletter_ID
我从混杂在一起的订阅表中得到第 2 行和第 4 行(在下面结果集的第 2 行中):
Person_ID Newsletter_ID Subscribed Updated Expr1
2 1 true 2010-05-01 2010-05-01
3 1 true 2010-05-01 2010-05-15
3 2 true 2010-05-10 2010-05-10
谢谢!
最佳答案
我最近遇到了一个有点类似的 problem .
我不是 SQL 专家,所以我不能就什么是最好的设计给出太多建议。但在专业人士介入之前,也许这会有所帮助:
SELECT s.Person_ID, s.Newsletter_ID
FROM (
SELECT MAX(ID) AS mid
FROM Subscriptions
GROUP BY
Person_ID,Newsletter_ID
) q
JOIN Subscriptions s
ON q.mid = s.ID
WHERE s.Subscribed = 1
请注意,我已将 ID 列添加到您的订阅表中(稍后我会解释原因)。
现在,让我们分析一下它是如何工作的(或者无论如何我认为它是如何工作的;如果我错了,我很乐意得到纠正)。
首先,您检索给定人员/时事通讯的所有记录。这就是子查询的作用(是的,我知道你说过你宁愿没有子查询,但我不确定你是否可以没有子查询)。我按 person_id 和 newsletter_id 分组。这可以返回多行。请注意,我选择了 MAX(ID)。如果您使用自动增量 ID,并且可以安全地假设 ID 列中编号最大的行是该组的最新行(即,如果您不手动插入 ID),则此子查询将为您获取最后一个 ID每个人/时事通讯的行。
因此,您可以将它与订阅表连接起来:连接条件是订阅行的 ID 必须与您从子查询中检索到的 MAX id 相匹配。在这里,您只考虑每个时事通讯/个人的最新记录。然后,您可以使用 WHERE 条件排除非事件订阅。
如果您想将结果限制为给定的时事通讯(或给定的人),请将该条件添加到 WHERE 子句中。
索引应该有助于使这个查询运行得更快。
希望这对您有所帮助。
已添加
如果出于某种原因你不能保证 MAX(Subscriptions.ID) 将对应于最后插入的行,你可能会做这样的事情(我认为它遵循相同的逻辑,但有点冗长并且可能效率较低):
SELECT Person_ID, Newsletter_ID
FROM (
SELECT MAX(Updated) AS upd, Newsletter_ID AS nid, Person_ID AS pid
FROM Subscriptions
GROUP BY
Person_ID,Newsletter_ID
) q
JOIN Subscriptions s
ON q.pid = s.Person_ID AND q.nid = s.Newsletter_ID and q.upd = s.Updated
WHERE Subscribed = 1
新编辑
再三考虑,我认为我添加的备选方案(带有 MAX(Updated)
的备选方案)是错误的。您无法确定子查询中选择的 Newsletter_ID 和 Person_ID 将是 MAX(Updated) 行对应的 Newsletter_ID 和 Person_ID。由于这些列用于连接条件,因此该查询可能会给出虚假结果。
关于SQL 查询 - 在组中选择 'last updated' 记录,更好的数据库设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2844951/