MySQL左连接需要一段时间

时间:2013-10-21 19:30:29

标签: mysql

我正在尝试获取可能的客户列表以及订单历史记录的总和(ltv)

如果没有订单,则此查询会在一秒钟内加载。使用order by,查询时间超过90秒。

SELECT a.customerid,a.firstname,a.lastname,Orders.ltv  
    FROM customers a
    LEFT JOIN (
        SELECT customerid,
               SUM(amount) as ltv 
        FROM orders 
       GROUP BY customerid) Orders 
     ON Orders.customerid=a.customerid  
ORDER BY 
   Orders.ltv DESC 
LIMIT 0,10

任何想法如何加速?

编辑:我想我清理了一下这个问题。该版本的查询实际上要复杂一些。其他数据从customers表中选择,也可以进行排序。

3 个答案:

答案 0 :(得分:4)

如果没有实际的架构,就很难知道数据是如何相关的,但我想这个查询应该是等效的并且性能更高:

SELECT a.customerid, coalesce(sum(o.amount), 0) TotalLtv FROM customers a
LEFT JOIN orders o ON a.customerid = o.cusomterid
GROUP BY a.customerid
ORDER BY TotalLtv DESC
LIMIT 10

coalesce将确保您为没有订单的客户返回0

正如@ypercube让我注意到的那样,amount上的索引也无济于事。你可以尝试一下:

ALTER TABLE orders ADD INDEX(customer, amount)

更新问题后

如果需要在select子句中添加功能上依赖于a.customerid的更多字段,则可以使用非标准MySQL group by子句。与a.customerid, a.firstname, a.lastname分组:

相比,这将带来更好的性能
SELECT a.customerid, a.firstname, a.lastname, coalesce(sum(o.amount), 0) TotalLtv
FROM customers a
LEFT JOIN orders o ON a.customerid = o.cusomterid
GROUP BY a.customerid
ORDER BY TotalLtv DESC
LIMIT 10

答案 1 :(得分:2)

这里有一些事情。首先,您似乎根本不需要加入customers表,因为您只将customerid用于orders表中已经存在的SELECT customerid, SUM(amount) AS ltv FROM orders GROUP BY customerid ORDER BY ltv DESC LIMIT 0,10 。如果您有超过10个具有相应金额的客户ID,您甚至不需要查看客户ID的列表,这些客户ID不具有您从客户处获得LEFT JOIN所获得的金额。因此,您应该能够减少对此的查询:

SELECT c.customerid, c.firstname, c.lastname, coalesce(o.ltv, 0) AS total
FROM customers AS c
LEFT JOIN (
    SELECT customerid, SUM(amount) as ltv
    FROM orders
    GROUP BY customerid
    ORDER BY ltv DESC LIMIT 0,10) AS o
ON c.customerid = o.customerid

您需要有关customerid的索引。不幸的是,排序是在计算字段上进行的,所以从那时起你可以做很多事情来加快速度。

我看到了更新的问题。由于您确实需要客户提供的其他字段,我将修改我的答案以包含客户表

{{1}}

请注意,我正在您在原始查询中加入一个子选择的表,但是我已经对子选择表执行了排序和限制,因此您不必对所有记录进行排序订单表上的条目。

答案 2 :(得分:0)

两件事。首先,不要使用内部查询。 MySQL确实允许在投影别名上使用ORDER BY。其次,你应该通过在组合键(customerid,amount)上使用B-TREE索引来获得相当大的改进。然后引擎将能够通过简单遍历索引来执行此查询,而无需获取任何行数据。