Mysql 优化sql查询

Mysql 优化sql查询,mysql,sql,Mysql,Sql,请帮助我优化下一个SQL查询 SELECT Executor_service.*, Executor.* FROM Executor_service INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id LEFT JOIN Cancel ON Cancel.executor_id = Executor.user_id AND Cancel.city_id = 3538 WHERE Cancel.order_

请帮助我优化下一个SQL查询

SELECT Executor_service.*, Executor.*
FROM Executor_service
INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id
LEFT JOIN Cancel ON Cancel.executor_id = Executor.user_id AND Cancel.city_id = 3538 
WHERE Cancel.order_id IS NULL
AND Executor.user_id != 236 
AND Executor_service.service_id = 511
AND Executor_service.status = 'free'
AND Executor_service.mstatus = 'work'
AND Executor_service.city_id =3538
AND Executor.balance > 0
AND Executor.status = 'free'
ORDER BY Executor.rate DESC
LIMIT 1
此代码非常慢(10-15秒)->>在Cancel.executor\u id=executor.user\u id和Cancel.city\u id=3538时左连接取消

更新1

我需要获取Executor.user\u id

表执行器具有下一个结构

表Executor_服务具有下一个结构

表Cancel具有下一个结构

表执行器是系统中存在的所有执行器

表Executor\u service是每个Executor的exist服务,其中Executor.user\u id=Executor\u service.Executor\u id

表Cancel是关于取消订单的现有信息(取消者和原因)

我需要从指定城市获得一个评级最高的执行者,而不是零余额,该城市根据执行者服务表(executor\u service.service\u id)中的指定id执行服务。 同时,Cancel表不应包含给定执行器为此服务被点击的记录

更新2

我改变

SELECT Executor_service.*, Executor.*
FROM Executor_service
INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id


需要3-5秒,但是查询时间太长了。

尝试以下索引:
执行者服务(城市id、服务id、状态、mstatus、用户id)


我猜
连接键已经有了索引。

因为您没有从表中选择任何要左连接的内容,所以可以完全忽略它。使用
internal JOIN
过滤结果,但
LEFT(OUTER)JOIN
不过滤结果,因此不需要:

  SELECT Executor_service.*, Executor.*
    FROM Executor_service
         INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id
   WHERE     Executor.user_id != 236
         AND Executor_service.service_id = 511
         AND Executor_service.status = 'free'
         AND Executor_service.mstatus = 'work'
         AND Executor_service.city_id = 3538
         AND Executor.balance > 0
         AND Executor.status = 'free'
ORDER BY Executor.rate DESC
    LIMIT 1
看看是否适合你。如果您真的打算在
Cancel
表上使用
LEFT JOIN
,那么这就是方法

性能优化的问题在于,您目前根本没有真正使用
左连接如果确实要使用LEFT JOIN,则不能在WHERE子句中使用该表的列。这会将该连接转换为内部连接。

另一件小事情:查看代码,最好通过公共
city\u id
列连接表
Cancel
Executor\u service
,而不是写入
city\u id=3538
两次:

带有这些更正的代码如下所示(保留内部联接中的取消表,与以前一样):

此外,如果您想使用
左连接
过滤结果,以便排除
取消
表中的条目(如注释中所述),则可以使用以下代码:

  SELECT Executor_service.*, Executor.*
    FROM Executor_service
         INNER JOIN Executor ON Executor.user_id = Executor_service.executor_id
   WHERE     Executor.user_id != 236
         AND Executor_service.service_id = 511
         AND Executor_service.status = 'free'
         AND Executor_service.mstatus = 'work'
         AND Executor_service.city_id = 3538
         AND Executor.balance > 0
         AND Executor.status = 'free'
         AND NOT EXISTS (SELECT Cancel.order_id
                           FROM Cancel
                          WHERE Cancel.executor_id = Executor.user_id AND Cancel.city_id = Executor_service.city_id)
ORDER BY Executor.rate DESC
    LIMIT 1
另一种方法是,您可以使用
LEFT JOIN
(在所选值中添加
Cancel.order\u id
)编写查询,然后在外部查询中将
移动到Cancel.order\u id为NULL的位置

TL;博士:
如果您确实想在
Cancel
表上使用内部联接,则可以完全忽略它。若要将其用作排除连接到
Cancel
表中现有数据的数据,则应将其重写为上一个代码中发布的数据(或通过子查询)。如果没有这些,那么在不知道代码背后的数据的情况下很难优化代码。。。在这种情况下,我至少希望这些小小的输入能帮助您更好地理解您真正想要的东西和您得到的东西。

稍微调整了查询结构。我更愿意看到表“A”直接连接到“B”和“B”到“C”的位置,等等。。我还尝试将左侧的table.column保留在左侧,将右侧的table.column保留在右侧。另外,对于特定于表的条件,我尝试直接将其放在连接处。这有助于我直观地了解条件的位置,以及在确定优化指标时可能有帮助的内容

SELECT 
      ES.*, 
      E.*
   FROM 
      Executor_service ES
         INNER JOIN Executor E
            ON ES.executor_id = E.user_id
            AND ES.status = E.status
            AND E.balance > 0
         LEFT JOIN Cancel C
            ON ES.executor_id = C.executor_id
           AND ES.City_ID = C.city_id
   WHERE 
          ES.service_id = 511
      AND ES.status = 'free'
      AND ES.mstatus = 'work'
      AND ES.city_id = 3538
      AND ES.executor_id != 236
      AND C.order_id IS NULL
   ORDER BY 
      E.rate DESC
   LIMIT 1
这就是说,有一对可传递关系(如果a=B和B=C,那么a=C),比如您对执行者的用户id的标准,它与executor_service.executor_id相同,所以我将其移到了“ES”(executor_service的别名)的WHERE子句中。更正您的取消代码的左连接,并在where中显式查找IS NULL

至于索引,我将确保以下内容对您有所帮助

Executor_Service table -- index on (service_id, city_id, mstatus, status )
Executor table -- index on ( user_id, status, balance ).  
Cancel table -- index on ( executor_id, city_id )

通过将余额包含在索引中,它为查询创建了一个覆盖索引,不需要转到数据页,并且可以直接从索引中获取值。

使用explain找出哪里需要索引执行计划以查看哪里浪费了时间,这也将有助于了解哪里缺少索引。您好。谢谢你的回复。我使用左联接是因为我需要检查Cancel表中是否没有条目。内部联接将返回一个空的response@PiuPiu如果那是你想做的,那就是你写得不对。您编写它的方式会将其转换为
内部联接
(如我所述)。我编辑了我的答案并添加了另一个代码示例,引用了这个。@PiuPiu,很高兴它起作用了。。。你的时间有哪些改进。
SELECT 
      ES.*, 
      E.*
   FROM 
      Executor_service ES
         INNER JOIN Executor E
            ON ES.executor_id = E.user_id
            AND ES.status = E.status
            AND E.balance > 0
         LEFT JOIN Cancel C
            ON ES.executor_id = C.executor_id
           AND ES.City_ID = C.city_id
   WHERE 
          ES.service_id = 511
      AND ES.status = 'free'
      AND ES.mstatus = 'work'
      AND ES.city_id = 3538
      AND ES.executor_id != 236
      AND C.order_id IS NULL
   ORDER BY 
      E.rate DESC
   LIMIT 1
Executor_Service table -- index on (service_id, city_id, mstatus, status )
Executor table -- index on ( user_id, status, balance ).  
Cancel table -- index on ( executor_id, city_id )