【问题标题】:Make multi-subselect more efficient?使多子选择更有效?
【发布时间】:2015-06-09 19:53:14
【问题描述】:

我有一个选择查询,它按字母顺序返回与给定 ID 相关的下一条记录:

SELECT * 
FROM contacts 
WHERE client_id = 22844 
AND deleted_at IS NULL 
AND 
( 
  ( 
    last_name = (
      SELECT last_name 
      FROM contacts 
      WHERE client_id = 22844 AND id = 717604
    ) 
    AND first_name > (
      SELECT first_name 
      FROM contacts 
      WHERE client_id = 22844 AND id = 717604
    )
  ) 
  OR ( 
    last_name > (
      SELECT last_name 
      FROM contacts 
      WHERE client_id = 22844 AND id = 717604
    ) 
  ) 
) 

ORDER BY last_name, first_name 
LIMIT 1

first_name、last_name、deleted_at 和 client_id 有单独的索引。

有什么方法可以重写它以提高效率吗?当前,当特定客户有大约 3000 个联系人时,大约需要 250 毫秒 - 300 毫秒。

目前使用mysql 5.5

编辑:

如果我忽略 deleted_at IS NULL 条件,结果会快一个数量级。我可能会停止使用软删除并将已删除的记录移至存档。

【问题讨论】:

    标签: mysql sql query-optimization


    【解决方案1】:

    这是另一种方法。它在看到所需行后枚举行:

    SELECT c.*
    FROM (SELECT c.*,
                 (@rn := if(c.id = 717604 or @rn > 0, @rn + 1, 0) as rn                
          FROM contacts c CROSS JOIN
               (SELECT @rn := 0) params
          WHERE c.client_id = 22844 AND c.deleted_at IS NULL 
          ORDER BY c.last_name, c.first_name
         ) c
    WHERE rn = 2;
    

    对于这个查询,您需要contacts(client_id, deleted_at, last_name, first_name) 上的索引。

    编辑:

    您的查询的性能似乎是合理的。但是,最好的索引是contacts(client_id, id, last_name)contacts(client_id, id, first_name)

    【讨论】:

    • 嗯,通过您的查询和索引,explain 仍然告诉我它正在使用intersect(client_id, deleted_at) 作为索引。我的查询也得到了相同的交叉索引,有或没有你建议的其他索引?
    • @jemminger。 . .好奇的。 where 子句本质上由两个相等条件组成,因此索引应该用于whereorder by。也许如果你删除了只有两列的索引,MySQL 会更聪明。
    • 目前实际上没有任何复合索引。它选择与两个单独的索引相交,client_iddeleted_at
    【解决方案2】:
    SELECT  b.*
        FROM  
          ( SELECT  last_name, first_name
                FROM  contacts
                WHERE  client_id = 22844
                  AND  id = 717604 
          ) AS a
        JOIN  contacts AS b
        WHERE  deleted_at IS NULL
          AND    b.last_name >= a.last_name
          AND  ( b.first_name > a.first_name
             OR  b.last_name > a.last_name 
               )
        ORDER BY  b.last_name, b.first_name
        LIMIT  1; 
    -- and have these indexes on contacts:
    INDEX(client_id, id),
    INDEX(last_name, first_name) 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-13
      • 2012-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多