fed*_*qui 3 mysql sql optimization performance greatest-n-per-group
按照我最近的问题从最后一项中选择信息并加入总金额,我在生成表时遇到了一些内存问题
我有两张桌子sales1,sales2像这样:
id | 日期| 客户| 拍卖
使用此表定义:
CREATE TABLE sales (
id int auto_increment primary key,
dates date,
customer int,
sale int
);
Run Code Online (Sandbox Code Playgroud)
sales1并且sales2具有相同的定义,但sales2有sale=-1在各个领域.客户可以在任何一个,一个或两个表中.两个表都有大约300.000条记录和比此处所示更多的字段(大约50个字段).他们是InnoDB.
我想为每个客户选择:
我使用的查询是:
SELECT a.customer, count(a.sale), max_sale
FROM sales a
INNER JOIN (SELECT customer, sale max_sale
from sales x where dates = (select max(dates)
from sales y
where x.customer = y.customer
and y.sale > 0
)
)b
ON a.customer = b.customer
GROUP BY a.customer, max_sale;
Run Code Online (Sandbox Code Playgroud)
问题是:
我必须得到结果,我需要进行某些计算,将日期分开:2012年的信息,2013年的信息,以及所有年份的信息.
每当我做一年时,存储所有信息大约需要2-3分钟.
但是当我尝试从这些年来收集信息时,数据库崩溃了,我得到的信息如下:
InternalError: (InternalError) (1205, u'Lock wait timeout exceeded; try restarting transaction')
Run Code Online (Sandbox Code Playgroud)
加入如此庞大的表似乎对数据库来说太过分了.当我explain查询时,几乎所有的时间百分比都来自creating tmp table.
我想在分开数据收集的季度.我们每三个月获得一次结果,然后加入并对其进行排序.但我想最终的连接和排序对于数据库来说太多了.
那么,只要我无法更改表结构,您会建议什么才能优化这些查询?
Bil*_*win 13
300k行不是一张巨大的表.我们经常看到3亿行表.
您的查询的最大问题是您正在使用相关子查询,因此它必须为外部查询中的每一行重新执行子查询.
通常情况下,您不需要在一个SQL语句中完成所有工作.将它分解为几个更简单的SQL语句有一些优点:
SELECT customer, COUNT(sale) AS number_of_purchases
FROM sales
GROUP BY customer;
Run Code Online (Sandbox Code Playgroud)
销售(客户,销售)索引最适合此查询.
这是经常出现的最大n组问题.
SELECT a.customer, a.sale as max_sale
FROM sales a
LEFT OUTER JOIN sales b
ON a.customer=b.customer AND a.dates < b.dates
WHERE b.customer IS NULL;
Run Code Online (Sandbox Code Playgroud)
换句话说,尝试将行匹配a到b具有相同客户和更大日期的假设行.如果找不到这样的行,那么a该客户必须拥有最大的日期.
销售指数(客户,日期,销售)最适合此查询.
如果您在最大的日期可能为客户进行多次销售,则此查询将为每个客户返回多个行.你需要找到另一个栏来打破平局.如果您使用自动增量主键,它适合作为平局,因为它保证是唯一的,并且它往往会按时间顺序增加.
SELECT a.customer, a.sale as max_sale
FROM sales a
LEFT OUTER JOIN sales b
ON a.customer=b.customer AND (a.dates < b.dates OR a.dates = b.dates and a.id < b.id)
WHERE b.customer IS NULL;
Run Code Online (Sandbox Code Playgroud)
SELECT customer, SUM(sale) AS total_purchases
FROM sales
WHERE sale > 0
GROUP BY customer;
Run Code Online (Sandbox Code Playgroud)
销售(客户,销售)索引最适合此查询.
您应该考虑使用NULL来表示缺少的销售值而不是-1.SUM()和COUNT()等聚合函数忽略NULL,因此您不必使用WHERE子句来排除sales <0的行.
回复:你的评论
我现在拥有的是一个包含字段年,季度,total_sale(关于对(年,季))和销售的表.我想收集的是有关特定时期的信息:本季度,季度,2011年...信息必须分配给顶级客户,销售额较大的客户等.是否有可能从客户那里获得最后的购买价值total_purchases大于5?
SELECT customer, SUM(sale) AS total_purchases
FROM sales
WHERE (year, quarter) = (2012, 4) AND sale > 0
GROUP BY customer
ORDER BY total_purchases DESC
LIMIT 5;
Run Code Online (Sandbox Code Playgroud)
我想根据实际数据对其进行测试,但我认为销售指数(年,季度,客户,销售)最适合此查询.
SELECT a.customer, a.sale as max_sale
FROM sales a
INNER JOIN sales c ON a.customer=c.customer
LEFT OUTER JOIN sales b
ON a.customer=b.customer AND (a.dates < b.dates OR a.dates = b.dates and a.id < b.id)
WHERE b.customer IS NULL
GROUP BY a.id
HAVING COUNT(*) > 5;
Run Code Online (Sandbox Code Playgroud)
与上面其他最大n组的查询一样,销售指数(客户,日期,销售)最适合此查询.它可能无法优化连接和组,因此这将产生临时表.但至少它只会做一个临时表而不是很多.
这些查询足够复杂.您不应该尝试编写可以提供所有这些结果的单个SQL查询.记住Brian Kernighan的经典引用:
每个人都知道调试的难度是首先编写程序的两倍.因此,如果你在编写它时就像你一样聪明,你将如何调试它?