SQL 查询 - 在组中选择 'last updated' 记录,更好的数据库设计?

标签 sql mysql history

假设我有一个包含 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 种查询。

  1. 查询显示截至查询时每个人的活跃订阅(我们可以假设更新永远不会在未来发生——因此,这意味着为每个 Person_ID-Newsletter_ID 返回具有最新“更新”值的记录对,只要 Subscribed 为真(如果 Person_ID-Newsletter_ID 对的最新记录的订阅状态为 false,那么我不希望返回该记录)。

  2. 返回特定时事通讯的所有事件订阅的查询 - 与 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/

相关文章:

MySQL 索引基数 - 性能与存储效率

java - 为什么Java可以接受多个参数但只能返回一个对象?

command-line - PowerShell历史记录:如何防止重复的命令?

mysql - 使用不同的条件连接同一个表上的不同行

php - 选择日期之前

java - 多个sql查询与Java二分查找的效率

mysql - 统计特定用户在 MySQL 中使用了多少数据

mysql - 我可以将 MysQL 时间戳与日期时间列进行比较吗?不好吗?

visual-studio-2013 - move 文件在 TFS 2013 中丢失历史记录

mysql - 在 SQL 结果中创建一个新列