mysql - 使用数学计算进行查询的性能问题

标签 mysql sql performance

这是我的查询及其性能(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_idsales_status 已在 tblSales 中编入索引。那么您对改进内部查询(计算成功率的地方)以提高性能有什么建议吗?我已经玩了两个多小时的数学了。但找不到更好的方法。

顺便说一句,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 等与我的问题无关。

最佳答案

许多“问题”,没有涉及“数学”。

JOIN 让事情变得困难。 LEFT JOIN 说“我不关心该行是否存在于'右'表中。(我怀疑你不需要LEFT??)但它也说“右表中可能有多行。根据列名称,我猜测每个offer_id只有一个offer_name。如果这是正确的,那么这是我的第一个建议。 (这将使优化器相信 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, ...

它还消除了一个错误,其中您假设内部ORDER BY将保留LIMIT。过去是这样,但在较新版本的 MariaDB/MySQL 中,情况并非如此。 “派生表”(您的子查询)中的 ORDER BY 现在被忽略

已减少 2 项,还有更多。

“不要隐藏函数中的索引列。”我指的是DATE(t.sales_time) = CURDATE()。假设您没有“ future ”的 sales_time 值,则可以将该测试更改为 t.sales_time >= CURDATE()。如果您确实需要限制为今天,请执行以下操作:

  AND sales_time >= CURDATE()
  AND sales_time  < CURDATE() + INTERVAL 1 DAY

ORDER BYLIMIT 通常应该放在一起。在您的情况下,您还可以将 LIMIT 添加到“派生表”,从而导致外部查询只能使用 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),但让我们进行“覆盖”(首先专门使用 sales_time):

INDEX(sales_time, sales_status, order_id)

如果tbloffersPRIMARY KEY(offer_id),那么没有进一步的索引值得添加。否则,添加此覆盖索引(按此顺序):

INDEX(offer_id, offer_name)

(向其他回答者道歉;我窃取了您的一些想法。)

关于mysql - 使用数学计算进行查询的性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48240474/

相关文章:

java - SelectOneMenu 没有任何可供选择的值

php - as3 使用 PDO 向 PHP 发送数据

mysql - 为什么 MySQL 查询在 VB.net 中将整数列读取为字节数组?

sql - 使用 T-SQL 将列动态转换为行

mysql - 为什么这个查询需要这么多时间以及如何加快速度?

php - php mysql中的计算

sql - 如何全范围均等地减少SQL查询的结果行数?

sql - 如何使用 T-SQL 组合多个不同长度的父子关系?

c++ - while 比 for 快吗?

performance - 使我的 List.filter 在 Scala 上更快?