如何重写MySQL查询

时间:2014-02-11 13:18:37

标签: mysql performance

我有以下查询,我正在尝试重写它以提高性能,我可以使用什么方法来重写它。

select 
    notes.id, notes.name, notes.parent_type, notes.contact_id from notes 
JOIN 
    ( 
    SELECT contact_id as id from accounts_contacts where account_id = 'acct1876' and deleted = '0' union 
    SELECT quote_id as id from quotes_accounts where account_id = 'acct1876' and deleted = '0' union 
    SELECT opportunity_id as id from accounts_opportunities where account_id = 'acct1876' and deleted = '0' union 
    SELECT leads.id as id from leads where account_id = 'acct1876' and deleted = '0' union 
    SELECT  project_id as id from projects_accounts where account_id = 'acct1876' and deleted = '0' union 
    select 'acct1876' as id 
    ) A 
    ON A.id = notes.parent_id and deleted = '0' OR contact_id in 
    ( SELECT contact_id from accounts_contacts where account_id = 'acct1876' and deleted = '0' ) and deleted = '0' 

    group by notes.id;

2 个答案:

答案 0 :(得分:1)

首先,你的最终OR是内连接开始的副本,否则毫无意义。

这部分

  ON A.id = notes.parent_id 
  and deleted = '0' 
  OR contact_id in ( SELECT contact_id 
                       from accounts_contacts 
                       where account_id = 'acct1876' 
                       and deleted = '0' ) 
  and deleted = '0' 

可以

  ON A.id = notes.parent_id 

接下来,您似乎正在尝试获取与给定帐户相关联的所有ID,包括相关帐户。我会确保每个表都有一个关于帐户ID和已删除列的索引。此外,对于此查询,我将它作为DISTINCT以防止重复项连接到notes表。然后我会交换订单(对我来说,在心理上查询你想要的ID,然后获得相关的注释)。下面是UNION查询的每个表的索引,以及加入的父ID列的notes表。

table                  index
accounts_contacts      (account_id, deleted, contact_id)
quotes_accounts        (account_id, deleted, quote_id )
accounts_opportunities (account_id, deleted, opportunity_id )
leads                  (account_id, deleted, id
projects_accounts      (account_id, deleted, project_id )
notes                  (parent_id)

现在,次要更新查询

select 
      notes.id, 
      notes.name, 
      notes.parent_type, 
      notes.contact_id 
   from 
         (SELECT DISTINCT contact_id as id 
             from accounts_contacts 
             where account_id = 'acct1876' and deleted = '0' 
          union 
          SELECT quote_id as id 
             from quotes_accounts 
             where account_id = 'acct1876' and deleted = '0' 
          union 
          SELECT opportunity_id as id 
             from accounts_opportunities 
             where account_id = 'acct1876' and deleted = '0' 
          union 
          SELECT leads.id as id 
             from leads 
             where account_id = 'acct1876' and deleted = '0' 
          union 
          SELECT project_id as id 
             from projects_accounts 
             where account_id = 'acct1876' and deleted = '0' 
          union 
          select 'acct1876' as id ) A 
         JOIN Notes
            ON A.id = notes.parent_id 
   group by 
     notes.id;

正如有人提到的那样,你有一个分组依据,但是没有总和或聚合的列,这将导致第一个条目被包括在内,并且由于出现了自动递增的ID列,所以无论帐户“ID”来自何处,都具有相同的价值。

答案 1 :(得分:0)

为了回答你的问题并提升表现,我想建议一种看似奇怪的方法。

如果您有合理数量的记录,请使用MORE SELECTS,而不是单个(和重)记录。您可以在数组中收集ID,或者(最好)在字符串中收集ID,然后使用逗号等分隔ID列表进行另一个查询。这(虽然听起来完全是胡说八道)有许多优点,几乎没有任何缺点:单独的SELECT运行速度快,然后让数据库服务器呼吸。当您执行一个SELECT时,其他表将不会被锁定(!!!),而一个大的select将在整个查询时锁定所有涉及的表。因此,这样做更容易,更易读:

$idlist = fetch_idlist("select id from users where name like 'John%'");
$result = fetch_all("select * from mails where userid in ($idlist)");

比这个:

$result = fetch_all("select * from mails left join users on users.id=mails.userid ....")

我希望尽管功能不存在,但意义很明显。这只是原则。所以在你的情况下,也许你想选择联系人,构建id列表,然后对笔记等单独查询,并在php中编写最终结果。

同样,这只适用于低于百万的记录数,你不能以非常高的数量吃掉所有内存。但关键在于:在高负载时,分别耗费时间的查询和让其他进程适合介于两者之间(相对)长时间锁定一大堆表无比好。

很抱歉,如果不是100%的答案,但我认为值得解释。