假设我有一个包含3个表的MySQL数据库:
表1:具有1列ID(int)的人员
表2:简报,1列ID(int)
表3:订阅,列为Person_ID(int),Newsletter_ID(int),订阅(bool),更新(日期时间)
Subscriptions.Person_ID指向一个人,而Subscription.Newsletter_ID指向一个时事通讯。因此,每个人可以同时对0个或更多个杂志订阅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,Person 1没有订阅,Person 2订阅了Newsletter 1,Person 3订阅了Newsletter 2. Person 3订阅了Newsletter 1一段时间,但现在不行了。
我正在尝试做两种查询。
从查询时间开始显示每个人的有效订阅的查询(我们可以假设更新将永远不会在将来 - 因此,这意味着为每个Person_ID-Newsletter_ID返回具有最新'updated'值的记录对,只要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时,该对的任何现有记录都将移动到Subscriptions_History(这样订阅table只包含任何Person_ID-Newsletter_ID对的最新状态更新?
我在Windows上使用.net,使用Linq进行此类查询会更容易(或相同或更难)吗?实体框架?
编辑:如果我使用此查询,会发生以下情况:
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
谢谢!
答案 0 :(得分:2)
将您的Subscriptions
分成两个表:
true
):Person_Id
| Newsletter_Id
答案 1 :(得分:2)
我最近遇到了类似的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(更新)行对应的Newsletter_ID和Person_ID。由于这些列用于连接条件,因此该查询可能会产生伪造结果。
答案 2 :(得分:1)
有序分析功能“是此类问题的标准方法.1M记录,没问题......当然,取决于您机器的功率。
MAX( Updated) OVER( PARTITION BY
您希望“max”)
SELECT
x.*
FROM
(
SELECT
Person_ID
, Newsletter_ID
--, Subscribed
, Updated
, MAX(Updated) OVER( PARTITION BY Person_ID, Newsletter_ID, Subscribed) AS myUpdated
FROM Subscriptions
) x
WHERE Updated = myUpdated
答案 3 :(得分:0)
我认为你的设计非常好。子查询没有固有的缓慢 - 如果是表达查询的最佳方式,请使用它们。
这是获取所有最新(即未被覆盖)指令的查询:
SELECT Person_ID, Newsletter_ID, Subscribed, MAX(Updated)
FROM Subscriptions GROUP BY Person_ID, Newsletter_ID
然后,您可以将此查询用作另一个查询的子查询,以获得所需内容。对于您的查询#1:
SELECT x.Person_ID, x.Newsletter_ID FROM
(SELECT Person_ID, Newsletter_ID, Subscribed, MAX(Updated)
FROM Subscriptions GROUP BY Person_ID, Newsletter_ID) x
WHERE x.Subscribed;
对于查询#2:
SELECT x.Person_ID FROM
(SELECT Person_ID, Newsletter_ID, Subscribed, MAX(Updated)
FROM Subscriptions GROUP BY Person_ID, Newsletter_ID) x
WHERE x.Subscribed AND x.Newsletter_ID = ?
您肯定希望Newsletter_ID
表中的Subscriptions
上有索引,因为此查询可能非常具有选择性。
编辑: 哎呀,子查询中的Subscriptions列可以来自任意行,而不是生成MAX(更新)的行。你必须重新加入原始表:
SELECT x.Person_ID, x.Newsletter_ID, y.Subscribed FROM
(SELECT Person_ID, Newsletter_ID, MAX(Updated) as MaxUpdated
From Subscriptions GROUP by Person_ID, Newsletter_ID) x
JOIN Subscriptions y WHERE x.Person_ID = y.Person_ID AND
x.Newsletter_ID = y.Newsletter_ID AND
x.MaxUpdated = y.Updated