我有一个表格foodbar,使用以下DDL创建。 (我正在使用mySQL 5.1.x)
CREATE TABLE foodbar (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
weight double not null,
created_at date not null
);
我有四个问题:
答案 0 :(得分:3)
我不明白为什么你需要合成密钥,所以我将改用这个表:
CREATE TABLE foodbar (
user_id INT NOT NULL
, created_at date not null
, weight double not null
, PRIMARY KEY (user_id, created_at)
);
我如何编写一个返回结果集的查询,该结果集给出了以下信息:user_id,weight_gain其中weight_gain是7天前记录的体重和体重之间的差异。
SELECT curr.user_id, curr.weight - prev.weight
FROM foodbar curr, foodbar prev
WHERE curr.user_id = prev.user_id
AND curr.created_at = CURRENT_DATE
AND prev.created_at = CURRENT_DATE - INTERVAL '7 days'
;
日期算术语法可能不对,但你明白了
我如何编写一个能够返回体重增加最多的前N个用户的查询(再说一周以上)。一种“明显”的方式可能是将上面问题1中获得的查询用作子查询,但不知何故选择前N个。
见上文,添加ORDER BY curr.weight - prev.weight DESC
和LIMIT N
最后两个问题:不要推测,检查执行计划。 (postgresql有EXPLAIN ANALYZE
,关于mysql的dunno)你可能会发现你需要索引参与WHERE
和JOIN
的列,而不是构成结果集的列。
答案 1 :(得分:1)
我认为“只是某个人”涵盖了你所要求的大部分内容,但我只想补充说,参与计算的索引列根本不可能对你有所帮助,除非它恰好是覆盖索引。
例如,如果我想按产品X * Y的顺序获取它们,则按X,Y排序以下行没有帮助:
X Y
1 8
2 2
4 4
产品会将它们命名为:
X Y Product
2 2 4
1 8 8
4 4 16
如果mySQL支持表中的计算列并允许对这些列建立索引,那么这可能会有所帮助。
答案 2 :(得分:1)
我同意just somebody
关于主键的问题,但对于你所说的关于重量计算的问题,你最好存储增量而不是重量:
CREATE TABLE foodbar (
user_id INT NOT NULL,
created_at date not null,
weight_delta double not null,
PRIMARY KEY (user_id, created_at)
);
这意味着您将用户初始权重存储在用户表中,当您将记录写入foodbar
表时,用户可以在此时提供权重,但查询会减去当前重量的初始重量。所以你会看到像:
user_id weight_delta
------------------------
1 2
1 5
1 -3
看着这一点,你知道用户1增加了4磅/公斤/石头/等等。
通过这种方式,您可以使用SUM,因为某人每天都可能进行称重 - 使用just somebody
的{{1}}等式无论时间跨度如何都无效。
在MySQL中获取顶部x很容易 - 使用LIMIT子句,但请注意,您提供了ORDER BY以确保正确应用限制。
答案 3 :(得分:0)
这并不明显,但是您尝试解决的问题中缺少一些重要信息。当您考虑进入此表的实际数据时,它会变得更加明显。问题是你不太可能每天都有一致的用户权重记录。所以你需要澄清一些关于确定'当前重量'和'重量x天前'的规则。我将假设以下简单的规则:
现在回答问题:
1& 2:使用上述额外规则提供了生成两个结果集的机会:当前权重和之前的权重:
目前的权重:
select rd.*,
w.Weight
from (
select User_id,
max(Created_at) AS Read_date
from Foodbar
group by User_id
) rd
inner join Foodbar w on
w.User_id = rd.User_id
and w.Created_at = rd.Read_date
同样的x天前阅读:
select rd.*,
w.Weight
from (
select User_id,
max(Created_at) AS Read_date
from Foodbar
where Created_at < DATEADD(dd, -7, GETDATE()) /*Or appropriate MySql equivalent*/
group by User_id
) rd
inner join Foodbar w on
w.User_id = rd.User_id
and w.Created_at = rd.Read_date
现在只需将这些结果作为子查询
加入select cur.User_id,
cur.Weight as Cur_weight,
prev.Weight as Prev_weight
cur.Weight - prev.Weight as Weight_change
from (
/*Insert query #1 here*/
) cur
inner join (
/*Insert query #2 here*/
) prev on
prev.User_id = cur.User_id
如果我没记错的话,使用MySql语法获得前N个权重增益就是简单地添加:
ORDER BY cur.Weight - prev.Weight DESC limit N
2&amp; 3:选择索引需要稍微了解查询优化器将如何处理查询:
在索引选择方面,重要的是您要过滤或加入的列。如果确定选择性,优化器将使用该索引(请注意,有时您的过滤器必须极选择性返回&lt; 1%的数据才算有用)。总是在导航索引的慢磁盘搜索时间与简单处理内存中的所有数据之间进行交易。
3:尽管权重在您显示的内容中具有显着特征,但唯一的相关性在于过滤(或选择)在#2中以获得前N个权重增益。这是一个基于大量查询和大量处理的复杂计算;因此,权重将提供零利益作为指数。
另一个注意事项是,即使对于#2,您也必须计算所有用户的体重变化,以确定哪些用户获得了最大的收益。因此,除非每个用户拥有大量读数,否则您将阅读该表的大部分内容。 (即,将使用表扫描来获取大量数据)
索引可以从中受益:
这意味着User_id上的索引,Created__at会很有用(如果这是聚集索引,那么更多)。
4:不,遗憾的是,在数学上不可能确定单个值H和W如何独立地确定产品的排序。例如。 H = 3&amp; W = 3小于5,但如果H = 5且W = 1,那么乘积3 * 3大于5 * 1。 您必须在该附加列上实际存储计算索引。但是,正如我在上面对#3的回答中指出的那样,它仍然不太可能证明是有益的。