我如何在mysql中优化这个查询?

Posted

技术标签:

【中文标题】我如何在mysql中优化这个查询?【英文标题】:How i can optimize this query in mysql? 【发布时间】:2014-06-30 12:46:34 【问题描述】:

我有一个这样的查询 -

    SELECT 
  CONCAT(u.name, u.deviceAlias),
  u.name,
  u.deviceAlias,
  j.description,
  u.description AS vlanDescription 
FROM
  vlaninterfaces u,
  interfacedescriptioninfo j 
WHERE u.interfacedescriptioninfoid = j.id
  AND u.deviceId = j.deviceId 
  AND u.interfaceNameAlias = j.name 
  AND j.toDevice NOT IN 
  (SELECT 
    deviceAlias 
  FROM
    devices 
  WHERE siteId = 12) 
  AND u.deviceAlias IN 
  (SELECT 
    deviceAlias 
  FROM
    devices 
  WHERE siteId = 12) 
  AND CONCAT(u.name, u.deviceAlias) NOT IN 
  (SELECT DISTINCT 
    CONCAT(v.name, fromDevice) 
  FROM
    vlaninterfaces v,
    interfacedescriptioninfo i 
  WHERE v.interfacedescriptioninfoid = i.id
    AND v.deviceId = i.deviceId 
    AND v.interfacenameAlias = i.name 
    AND v.deviceAlias IN 
    (SELECT 
      deviceAlias 
    FROM
      devices 
    WHERE siteId = 12) 
    AND v.interfacenameAlias LIKE '%ae%' 
    AND IF(
      i.toDevice IS NULL,
      i.interconnectedDevice,
      i.toDevice
    ) IN 
    (SELECT 
      deviceAlias 
    FROM
      devices 
    WHERE deviceClass = 'SWITCH' 
      AND siteId = 12)) 
GROUP BY CONCAT(u.name, u.deviceAlias) ;

我尝试使用 MINUS 但在 mysql 中发现我们不能使用这样的集合运算符。

在解释计划中,使用不使用键并扫描所有行的表 vlaninterfaces。

请通过示例告诉我 NOT IN 的替代方法(如果可能,请使用相同的查询)。

【问题讨论】:

【参考方案1】:

您的查询很复杂,所以我只展示一部分。可以将 LEFT JOIN 加入表并检查 null 而不是使用 NOT IN。 IE。如果我们有查询:

 SELECT 
  CONCAT(u.name, u.deviceAlias),
  u.name,
  u.deviceAlias,
  j.description,
  u.description AS vlanDescription 
FROM
  vlaninterfaces u,
  interfacedescriptioninfo j 
WHERE u.interfacedescriptioninfoid = j.id
  AND u.deviceId = j.deviceId 
  AND u.interfaceNameAlias = j.name 
  AND j.toDevice NOT IN 
  (SELECT 
    deviceAlias 
  FROM
    devices 
  WHERE siteId = 12) 

然后我们可以使用 siteId 12 加入设备别名,并检查在 where 子句中加入是否成功(注意在下面的示例中,我还将 vlaninterfaces 和 interfacedescriptioninfo 上的加入更改为显式 - 我发现这更具可读性):

SELECT 
  CONCAT(u.name, u.deviceAlias),
  u.name,
  u.deviceAlias,
  j.description,
  u.description AS vlanDescription 
FROM
  vlaninterfaces u
  INNER JOIN interfacedescriptioninfo j ON (u.interfacedescriptioninfoid = j.id AND u.deviceId = j.deviceId AND u.interfaceNameAlias = j.name )
  LEFT JOIN devices da ON (j.toDevice = da.deviceAlias AND da.siteId = 12)
WHERE 
   da.deviceAlias IS NULL

【讨论】:

【参考方案2】:

好的,这需要大量的试验和错误,但我总是尝试将 IN 子句重写为连接,从而避免隐式子查询。 NOT IN 也是如此,您可以用 LEFT JOIN + WHERE 替换为 NULL

SELECT 
  CONCAT(u.name, u.deviceAlias),
  u.name,
  u.deviceAlias,
  j.description,
  u.description AS vlanDescription 
FROM
  vlaninterfaces u 
  JOIN   interfacedescriptioninfo j ON u.interfacedescriptioninfoid = j.id AND u.deviceId = j.deviceId AND u.interfaceNameAlias = j.name 
  JOIN devices d2 ON d2.deviceAlias=u.deviceAlias AND d2.siteId=12
  LEFT JOIN devices d1 ON d1.deviceAlias=j.toDevice AND d1.siteId = 12
  LEFT JOIN  (SELECT DISTINCT v.name, fromDevice
              FROM
              vlaninterfaces v,
              JOIN  interfacedescriptioninfo i ON v.interfacedescriptioninfoid = i.id   AND v.deviceId = i.deviceId   AND v.interfacenameAlias = i.name 
              JOIN devices d3 ON d3.deviceAlias=v.deviceAlias AND d3.siteId=12
              JOIN devices d4 ON d4.deviceAlias=IF(i.toDevice IS NULL,  i.interconnectedDevice,  i.toDevice) AND d4.siteId=12 AND d4.deviceClass = 'SWITCH' 
              WHERE v.interfacenameAlias LIKE '%ae%' 
            ) as subquery ON u.name=subquery.name AND u.deviceAlias=subquery.fromDevice

WHERE d1.deviceAlias is NULL -- this replaces the first NOT IN
and subquery.name is NULL  -- this replaces the second NOT IN

通过两个字段的连接查询实际上是丢弃索引。你不需要这样做。

最后,通过 name 和 deviceAlias 的 concat 分组在这里没有意义,因为您在 select 中没有任何聚合函数。

【讨论】:

以上是关于我如何在mysql中优化这个查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何优化mysql查询

如何使用索引优化我的 MySQL 查询

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

如何优化这个 MySql 查询 - 连接 3 个表?

mysql中如何根据经纬度优化这个距离查询

MySQL查询慢不知道如何优化