我该如何优化这个mysql查询?

时间:2011-12-14 16:46:12

标签: mysql optimization join

我有这个查询,但需要太长时间,通过NaviCat大约需要30秒。如果有可能,如何进行优化?

SELECT DISTINCT c.clientid, c.name, c.email, c.region 
FROM clients c RIGHT JOIN orders o ON c.clientid = o.clientid 
WHERE o.order_status = 'pending' 
AND c.clientid NOT IN (
    SELECT DISTINCT c.clientid 
    FROM clients c, orders o
    WHERE c.clientid = o.clientid AND o.order_status = 'paid'
    ) 
ORDER BY c.id DESC

更好地了解我需要的东西:我有两张桌子:

clients (id, clientid, name, email, region) 
orders (id, orderid, clientid, order_amount, order_status, ….)

记录示例:

Client | Order | Status
-----------------------
C1     | O1    | (paid)
C1     | O2    | (pending)
C2     | O3    | (paid)
C3     | O4    | (pending)
C4     | O5    | (paid)
C5     | O6    | (pending)

我只需要返回C3C5

非常感谢你的回答。

5 个答案:

答案 0 :(得分:1)

不确定这是如何工作的,但请尝试以下方法:

SELECT DISTINCT c.clientid, c.name, c.email, c.region 
FROM clients c
RIGHT JOIN orders o ON c.clientid = o.clientid AND o.order_status = 'pending'
LEFT JOIN orders o2 ON o.clientid = o2.clientid AND o.order_status = 'paid'
WHERE o2.clientid IS NULL

基本上,尝试匹配待处理订单和付款订单,并仅采用未通过的挂单。

在专业方面,您没有百万个子查询。一个con是WHERE剔除它们之前生成的行数可能要大得多。所以我不知道它是帮助还是伤害。

编辑:另外,是的,就像评论中的@ruakh一样,我想知道为什么RIGHT JOIN那里......订单可以没有客户,或者我错过了什么?

答案 1 :(得分:1)

有很多方法,这里有一个诀窍: -

SELECT c.clientid, c.name, c.email, c.region,
  SUM(IF(o.order_status = 'paid', 1, 0)) as paid
FROM clients c
INNER JOIN orders o 
ON c.clientid = o.clientid 
WHERE o.order_status IN( 'pending', 'paid')
GROUP BY c.clientid
HAVING paid = 0;

答案 2 :(得分:1)

这里有一些很棒的想法,但是在不知道数据库引擎中发生了什么的情况下尝试优化查询并不是获得最佳答案的最直接途径。有时优化只需要一个额外的索引,而不是SQL的更改。

您应该做的第一件事是查看解释计划(documentation for 5.1),然后决定是否可以更改查询或添加索引或其他内容。可能提供的答案之一是正确的,但没有执行计划,你只是猜测。

您的查询有很多想法。

我不明白为什么你需要RIGHT JOIN。由于你是在客户之后,内部联合就足够了。

任何使用DISTINCT或GROUP BY的查询都需要最终排序。如果需要排序的行数(客户端x订单)很大,则会影响性能。如果是@ypercube的方法可能会很好,否则@ ajreal的诀窍看起来很有希望。祝你好运。

编辑:这是一种有趣的blog关于此类查询和几种方法。

答案 3 :(得分:0)

这样的事情会更好:

SELECT DISTINCT c.clientid, c.name, c.email, c.region 
    FROM clients c 
INNER JOIN orders o ON c.clientid = o.clientid 
LEFT OUTER JOIN (
    SELECT cc.clientid FROM clients cc 
        INNER JOIN orders oo WHERE cc.clientid = oo.clientid AND      
        oo.order_status = 'paid'
    GROUP BY cc.clientid) cp ON cp.clientid = c.clientid
WHERE o.order_status = 'pending' 
AND cc.clientid IS NULL
ORDER BY c.id DESC

如果您的表很大,您不希望在查询中使用IN或OR,它们将不允许MySQL使用索引,而且,在您的子查询中,您没有使用内连接,这是错误的。

答案 4 :(得分:0)

使用EXISTS

SELECT c.clientid, c.name, c.email, c.region 
FROM clients c 
WHERE EXISTS
      ( SELECT *
        FROM orders o 
        WHERE o.clientid = c.clientid 
          AND o.order_status = 'pending'
      ) 
  AND NOT EXISTS
      ( SELECT *
        FROM orders o 
        WHERE o.clientid = c.clientid 
          AND o.order_status = 'paid'
      ) 
ORDER BY c.id DESC

使用JOIN

SELECT c.clientid, c.name, c.email, c.region 
FROM clients c 
  JOIN orders o
    ON  o.clientid = c.clientid 
    AND o.order_status = 'pending'
  LEFT JOIN orders o2
    ON  o2.clientid = c.clientid 
    AND o2.order_status = 'paid'
WHERE o2.clientid IS NULL
GROUP BY c.clientid
ORDER BY c.id DESC

我不明白为什么你有两个列似乎在两个表(id和表clientid中的client中提供相同目的(主键)的原因在表order)中。