我如何在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中优化这个查询?的主要内容,如果未能解决你的问题,请参考以下文章