【问题标题】:optimise IN subquery优化 IN 子查询
【发布时间】:2016-02-03 14:22:44
【问题描述】:

我有一个workers 表和一个关联的workerGeofence 表。

CREATE TABLE IF NOT EXISTS `workergeofences` (
`ID` int(11) NOT NULL,
  `WorkerID` varchar(20) NOT NULL,
  `GeofenceID` int(11) NOT NULL,
  `isActive` tinyint(4) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=latin1;

我只需要返回 workerGeofences 表中至少有一个 isActive 为 1 的条目的工人。

我能够通过以下方式获得预期的结果:

    SELECT distinct w.ID, Title, FName, SName, Email, Birthday, Address, Phone, description,
 companyID 
FROM Workers w WHERE companyID = ? 
and w.ID IN (SELECT WorkerID FROM WorkerGeofences WHERE isActive <> 0)
    limit ?,10

但是 in 子查询是详尽的,因为当我运行解释时,我可以看到它正在扫描整个表。我该如何解决这个问题?

【问题讨论】:

  • 你不能只告诉我们问题的一半。

标签: mysql sql join query-optimization in-subquery


【解决方案1】:

您在正确的轨道上,但您不应该需要select distinct。这会减慢查询速度,除非您知道有重复项——这不太可能,因为您选择的是WOrkers.Id

SELECT w.* 
FROM Workers w 
WHERE w.companyID = ? AND
      EXISTS (SELECT 1
              FROM workerGeofences wg
              WHERE w.ID = wg.WorkerID AND wg.isActive <> 0
             )
LIMIT ?, 10;

然后,对于这个查询,您需要Workers(CompanyId, Id)workerGeofences(WorkerId, isActive) 上的索引。

注意:为方便起见,我只是输入了select *。我假设所有列都来自Workers 表。

【讨论】:

  • (这比公认的答案更好,因为(1)它强调EXISTS,而不是慢速IN 和(2)它提到可能有帮助的索引。)
  • @RickJames 是的,我投了赞成票,但在发布之前我已经接受了另一个答案。谢天谢地,我已经在使用索引,并在接受的答案中选择了存在选项
  • @GordonLinoff 一个问题:您检查 isActive 0 而不是 isActive = 1 有什么原因吗?我认为在查询中检查 的成本更高?
  • 优化器无法知道isActive 可以接受多少个不同的值。你从&lt;&gt; 0 开始,Gordon 复制了你。然而,它可能效率低下——它涉及两个范围“ 0”。 =1 效率更高,因为它只有一个范围“= 1”。所以 Sagi 的第三个例子有这样的改进。
  • 实际上,不等式应该没有什么区别。它是索引中的第二个键,因此不等式仍会使用索引(等式会稍微好一些,但这是一种微优化)。
【解决方案2】:

首先,你的加入是错误的!您没有比较两个表上的任何公共列,您应该像这样添加 where workerGeofences.workerID = w.id:

SELECT  w.ID, Title, FName, SName, Email, Birthday, Address, Phone,
        description, companyID
    FROM  Workers w
    join  workerGeofences
    WHERE  workerGeofences.workerID = w.ID companyID = ?
      and  w.ID IN (
        SELECT  WorkerID
            FROM  WorkerGeofences s
            WHERE  isActive <> 0
              and  s.workerID = w.id
                   )
    limit  0,10 

其次,您没有从第二个表中选择任何内容,因此连接是不必要的,并且在您的 IN 语句中,您没有比较正确的 ID,因此您的查询应该是:

SELECT  w.ID, Title, FName, SName, Email, Birthday, Address, Phone,
        description, companyID
    FROM  Workers w
    WHERE  companyID = ?
      and  w.ID IN (
        SELECT  WorkerID
            FROM  WorkerGeofences s
            WHERE  isActive <> 0
              and  s.workerID = w.ID
                   )
    limit  0,10 

此外,您可以为此使用 EXISTS()。

SELECT  w.ID, Title, FName, SName, Email, Birthday, Address, Phone,
        description, companyID
    FROM  Workers w
    WHERE  companyID = ?
      and  exists 
      ( SELECT  1
            FROM  WorkerGeofences s
            WHERE  isActive = 1
              and  s.workerID = w.ID
      )
    limit  0,10

【讨论】:

  • 使用 EXISTS 并删除 DISTINCT.. 或使用 JOIN。
  • 我使用的存在,我不能删除不同的,因为我们不知道他的表中是否有重复@Arth
  • 假设每个工作人员行在工作人员中都是唯一的(相当安全的假设),您可以删除 DISTINCT。
【解决方案3】:

为了完整性,使用 JOIN:

SELECT DISTINCT w.ID,
       w.Title, 
       w.FName, 
       w.SName, 
       w.Email, 
       w.Birthday, 
       w.Address, 
       w.Phone, 
       w.description, 
       w.companyID
  FROM Workers w 
  JOIN WorkerGeofences wg
    ON wg.workerID = w.id
   AND wg.isActive = 1
 WHERE w.companyID = ? 
 LIMIT ?,10

【讨论】:

  • 如果一个worker有多个isActive行,它会产生重复的行。然后DISTINCT 开始删除重复文件——从而使其效率低于EXISTS 解决方案。
  • @RickJames 是的,你可能是对的.. 虽然我认为优化器可能会导致相同的查询。为了完整起见,我将其包含更多,但该评论将对旅行的眼睛有用,
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多