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

我应该如何优化这个mysql查询?,mysql,optimization,join,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.client

我有这个查询,但它太长了,通过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)
我只需要返回
C3
C5


非常感谢您的回答。

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

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
基本上,试着匹配一个待处理订单和一个已支付订单,并且只接受失败的待处理订单

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


编辑:还有,是的,就像评论中的@ruakh一样,我想知道为什么
右连接在那里。。。订单可以没有客户,或者我遗漏了什么吗?

不确定这将如何工作,但请尝试以下操作:

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
基本上,试着匹配一个待处理订单和一个已支付订单,并且只接受失败的待处理订单

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


编辑:还有,是的,就像评论中的@ruakh一样,我想知道为什么
右连接在那里。。。订单可以没有客户,还是我遗漏了什么?

有很多方法,这里有一个技巧:-

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;

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

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;

这样做会更好:

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

如果您的表很大,您不想在或或查询中使用,它们将不允许MySQL使用索引,另外,在子查询中您没有使用内部联接,这是错误的。

类似这样的方式会更好:

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

如果您的表很大,您不想在或或查询中使用,它们将不允许MySQL使用索引,另外,在子查询中您没有使用内部联接,这是错误的。

使用
存在

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
使用
连接

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
在table
client
和table
order

使用
存在

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
使用
连接

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
在table
client
和table
order

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

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

对你的问题有几点想法

我不明白你为什么需要正确的加入。因为您是在客户机之后,所以内部连接就足够了

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


编辑:这里有一个关于这种类型的查询和几种方法的有趣的例子。

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

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

对你的问题有几点想法

我不明白你为什么需要正确的加入。因为您是在客户机之后,所以内部连接就足够了

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


编辑:这里有一个关于这种类型的查询和几种方法的有趣的例子。

为什么要检查两次
o.order\u status=
?是否每个订单都有一个状态?是否确实要进行
右联接
?似乎您真的需要一个
内部联接
。(实际上,您似乎真的想要在
子句中使用
,但我可以想象性能原因迫使您使用
加入
。@ajreal:每个订单都有一个状态,但一个客户机可以有多个订单。OP希望找到每个有“待定”订单且没有任何“付费”订单的客户机。如果您想要优化,还应该提供表的定义和索引。我不明白的是,为什么两个表中有两列似乎具有相同的用途(主键)(
id
clientid
在表
client
中)为什么要在
o.order\u status=
上检查两次?每个订单都有一个状态吗?你确定要进行
右连接吗?看起来你真的想要
内部连接。(实际上,您似乎真的想要在
子句中使用
,但我可以想象性能原因迫使您使用
加入
。@ajreal:每个订单都有一个状态,但一个客户机可以有多个订单