我该如何改进这个MySQL查询?

时间:2013-10-29 15:13:16

标签: mysql

我正在尝试提高此查询的性能,因为它需要3-4秒才能执行。 这是查询

SELECT SQL_NO_CACHE
ac.account_id,
ac.account_name,
cl.name AS client_name,
IFNULL(cn.contact_number, "") AS Phone
FROM accounts AS ac
STRAIGHT_JOIN clients AS cl ON cl.client_id = ac.client_id
LEFT JOIN (
  SELECT bc.contact_number, bc.account_id
  FROM contact_numbers AS bc
  INNER JOIN (
    SELECT account_id, MAX(number_id) AS number_id
    FROM contact_numbers
    WHERE status = 1 AND contact_type != "Fax" AND contact_link = "Account"
    GROUP BY account_id
  ) AS bb ON bb.number_id = bc.number_id
) AS cn ON ac.account_id = cn.account_id

WHERE ac.status = 1
ORDER BY ac.account_name
LIMIT 0, 100

客户端表包含大约10行,这就是我直接加入的原因。帐户表包含350K记录。 contact_numbers包含大约500k条记录 我相信这里的问题是左连接和ORDER BY,但我不知道如何解决它。我也在使用SQL_NO_CACHE,因为帐户,contact_numbers表正在快速更新。

我还可以做些什么来改善此查询的效果?

这是此查询解释的屏幕截图

enter image description here

我正在使用MySQL 5.6.13 我设置sort_buffer_size = 1M 我的服务器有32GB的RAM

2 个答案:

答案 0 :(得分:0)

  • 以下内容应该使外部查询运行而不需要文件排序。

    CREATE INDEX ac_status_acctname ON accounts (status, account_name);
    
  • 下面应该创建内部查询Using index,并帮助它在不使用临时表的情况下执行GROUP。

    CREATE INDEX cn_max ON contact_numbers (account_id, status, contact_link, 
      contact_type, number_id);
    
  • 您需要加入account_id和number_id才能获得每个帐户最大的条目。你现在拥有它的方式,你只是得到任何帐户恰好具有相同的number_id,这可能不是你想要的,它可能是为子查询结果集生成太多行。

     bc INNER JOIN ... bb ON bb.account_id = bc.account_id AND bb.number_id = bc.number_id
    

    您也可以将相同的连接条件写为:

     bc INNER JOIN ... bb USING (account_id, number_id)
    

答案 1 :(得分:0)

我会实际重写查询。您当前选择了许多不需要的数据并将其丢弃。我会尽量减少获取数据的数量。

您似乎基本上为具有特定状态的每个帐户选择了一些内容,并且只占用了其中的100个。所以我会把它放在一个子查询中:

SELECT 
  account_id,
  account_name,
  c.name AS client_name,
  IFNULL(contact_number, '') as Phone
FROM (
  SELECT 
    account_id,
    MAX(number_id) as number_id
  FROM (
    SELECT account_id
    FROM accounts
    WHERE status = 1 -- other conditions on accounts go here
    ORDER BY account_name
    LIMIT 0, 100) as a
  LEFT JOIN contact_numbers n
    ON a.coount_id = n.account_id 
      AND n.status = 1 
      AND contact_type != "Fax" 
      AND contact_link = "Account"
  GROUP BY account_id) an
LEFT JOIN contact_numbers USING (account_id, number_id)
JOIN accounts a USING (account_id)
JOIN clients c USING (client_id);

(status, account_name)表(对于client_id = 4 accounts的查询)和contact_numbers中(status, client_id, account_name)的索引需要account_id索引。这应该足够了。