这是我的查询及其性能(slow_query_log):
SELECT j.`offer_id`, o.`offer_name`, j.`success_rate`
FROM
(
SELECT
t.`offer_id`,
(
SUM(CASE WHEN `offer_id` = t.`offer_id` AND `sales_status` = 'SUCCESS' THEN 1 ELSE 0 END) / COUNT(*)
) AS `success_rate`
FROM `tblSales` AS t
WHERE DATE(t.`sales_time`) = CURDATE()
GROUP BY t.`offer_id`
ORDER BY `success_rate` DESC
) AS j
LEFT JOIN `tblOffers` AS o
ON j.`offer_id` = o.`offer_id`
LIMIT 5;
# Time: 180113 18:51:19
# User@Host: root[root] @ localhost [127.0.0.1] Id: 71
# Query_time: 10.472599 Lock_time: 0.001000 Rows_sent: 0 Rows_examined: 1156134
此处,tblOffers
列出了所有优惠。 tblSales
包含所有销售。根据成功率(即那些成功的销售额),我们试图找出最畅销的优惠。
查询工作正常,并提供我需要的输出。但似乎它有点慢。
offer_id
和sales_status
已在tblSales
中编入索引。那么你对改进内部查询(计算成功率)有什么建议吗?这样可以提高性能?我一直在玩数学超过2小时。但无法获得更好的方式。
顺便说一下,tblSales
有很多数据。它包含那些成功,失败,待处理等的销售。
谢谢
修改
正如您所要求的,我也包括表格设计(仅包括相关字段):
tblSales
`sales_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`offer_id` bigint UNSIGNED NOT NULL DEFAULT '0',
`sales_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`sales_status` ENUM('WAITING', 'SUCCESS', 'FAILED', 'CANCELLED') NOT NULL DEFAULT 'WAITING',
PRIMARY KEY (`sales_id`),
KEY (`offer_id`),
KEY (`sales_status`)
此表中还有一些其他字段,其中包含其他一些信息。金额,user_id等与我的问题无关。
答案 0 :(得分:1)
许多问题', none 涉及" math"。
JOINs
让事情变得困难。 LEFT JOIN
说"我不在乎该行是否存在于'右边'表。 (我怀疑你不需要LEFT
??)但它也说"右表中可能有多行。根据列名称,我将猜测每个offer_name
只有一个offer_id
。如果这是正确的,那么这是我的第一个建议。 (这将使优化器相信JOIN
没有问题。)从
SELECT ..., o.offer_name, ...
LEFT JOIN `tblOffers` AS o ON j.`offer_id` = o.`offer_id`
...
到
SELECT ...,
( SELECT offer_name FROM tbloffers WHERE offer_id j.offer_id
) AS offer_name, ...
它还摆脱了 bug ,其中你假设将为ORDER BY
保留内部LIMIT
。过去就是这种情况,但在较新版本的MariaDB / MySQL中,不是。 "派生表中的ORDER BY
" (您的子查询)现在被忽略。
2下来,还有几个去。
"不要在函数中隐藏索引列。"我指的是DATE(t.sales_time) = CURDATE()
。假设您的未来没有sales_time
值,那么该测试可以更改为t.sales_time >= CURDATE()
。如果你真的需要限制到今天,那么这样做:
AND sales_time >= CURDATE()
AND sales_time < CURDATE() + INTERVAL 1 DAY
ORDER BY
和LIMIT
通常应放在一起。在您的情况下,您也可以将LIMIT
添加到&#34;派生表&#34;,从而导致只有5行供外部查询使用。但是......仍然存在让它们正确排序的问题。所以从
SELECT ...
FROM ( SELECT ...
ORDER BY ... )
LIMIT ...
到
SELECT ...
FROM ( SELECT ...
ORDER BY ...
LIMIT 5 ) -- trim sooner
ORDER BY ... -- deal with the loss of ordering from derived table
将它们全部整合起来,我有
SELECT j.`offer_id`,
( SELECT offer_name
FROM tbloffers
WHERE offer_id = j.offer_id
) AS offer_name,
j.`success_rate`
FROM
( SELECT t.`offer_id`,
AVG(t.sales_status = 'SUCCESS') AS `success_rate`
FROM `tblSales` AS t
WHERE t.sales_time >= CURDATE()
GROUP BY t.`offer_id`
ORDER BY `success_rate` DESC
LIMIT 5
) AS j
ORDER BY `success_rate` DESC;
(我冒昧地以两种方式缩短SUM(...)
。)
现在索引......
tblSales
至少需要(sales_time)
,但让我们去&#34;覆盖&#34; (首先是sales_time
):
INDEX(sales_time, sales_status, order_id)
如果tbloffers
有PRIMARY KEY(offer_id)
,则无法再添加其他索引。否则,添加此覆盖索引(按此顺序):
INDEX(offer_id, offer_name)
(向其他回答者道歉;我偷了你的一些想法。)
答案 1 :(得分:0)
根据您提供的信息不多(我的意思是表格架构),您可以尝试以下方法。
SELECT `o`.`offer_id`, `o`.`offer_name`, SUM(CASE WHEN `t`.`sales_status` = 'SUCCESS' THEN 1 ELSE 0 END) AS `success_rate`
FROM `tblOffers` `o`
INNER JOIN `tblSales` `t`
ON `o`.`offer_id` = `t`.`offer_id`
WHERE DATE(`t`.`sales_time`) = CURDATE()
GROUP BY `o`.`offer_id`
ORDER BY `success_rate` DESC
LIMIT 0,5;
您可以在此SQL Fiddle示例
中找到此查询的示例答案 2 :(得分:0)
这里,tblOffers列出了所有的OFFERS。并且tblSales包含所有销售。根据成功率(即那些成功的销售额),我们试图找出最畅销的优惠。
使用简单的JOIN
和GROUP BY
:
SELECT s.offer_id, o.offer_name,
AVG(s.sales_status = 'SUCCESS') as success_rate
FROM tblSales s JOIN
tblOffers o
ON o.offer_id = s.offer_id
WHERE s.sales_time >= CURDATE() AND
s.sales_time < CURDATE() + INTERVAL 1 DAY
GROUP BY s.offer_id, o.offer_name
ORDER BY success_rate DESC;
注意:
tblSales(sales_time)
上使用索引 - 或者更好tblSales(salesTime, offer_id, sales_status)
。success_rate
的算法已经简化 - 尽管这对性能的影响很小。offer_name
添加了GROUP BY
。如果您正在学习SQL,则应始终在GROUP BY
子句中包含所有未聚合的键。LEFT JOIN
中的优惠不在tblSales
时才需要tblOffers
。我猜你定义了正确的外键关系,但事实并非如此。答案 3 :(得分:0)
在不知道你的架构的情况下,我看到的最低悬的果实是这部分......
WHERE DATE(t.`sales_time`) = CURDATE()
尝试将其更改为类似
的内容Where t.sales_time >= @12-midnight-of-current-date and t.sales_time <= @23:59:59-of-current-date