Mysql子查询优化?

Posted

技术标签:

【中文标题】Mysql子查询优化?【英文标题】:Mysql Subquery Optimization? 【发布时间】:2014-05-25 03:15:53 【问题描述】:

我可以以任何不同的方式使用此代码来获得更好的性能吗?

我试过这种方式;

SELECT K.id, M.m_id, K.Sil, K.Banli, K.Foto, K.KullaniciAdi, K.Ad, K.Soyad, K.Gender,
(select rate_id,Rate,Zaman from ratings where UyeID=K.id ORDER BY rate_id DESC LIMIT 1)

但我不能做我想做的事。

这是工作代码。给出我想要的结果:

SELECT K.id, M.m_id, K.Sil, K.Banli, K.Foto, K.KullaniciAdi, K.Ad, K.Soyad, K.Gender,
       (  SELECT R.mr_id FROM mesajlarreply R 
          WHERE R.CID=M.m_id 
          ORDER BY R.mr_id DESC LIMIT 1
       ) as mr_id,
       (  SELECT R.Mesaj FROM mesajlarreply R 
          WHERE R.CID=M.m_id 
          ORDER BY R.mr_id DESC LIMIT 1
       ) as Mesaj,
       (  SELECT R.KayitZaman FROM mesajlarreply R 
          WHERE R.CID=M.m_id 
          ORDER BY R.mr_id DESC LIMIT 1
       ) as KayitZaman
FROM mesajlar M, kullanicilar K
WHERE
     CASE
         WHEN M.K1 =  '1'  THEN M.K2 = K.id
         WHEN M.K2 =  '1'  THEN M.K1 = K.id
     END
   AND ( M.K1 =  '1'  OR M.K2 =  '1' )
ORDER BY M.m_id DESC 
LIMIT 20

【问题讨论】:

你能发布两个表的结构吗?或者至少请附加有关主键主键的信息 - 这两个表中哪些列是主键? 您的编辑使这看起来是一个新的/不同的查询。如果是这样,您应该能够针对新查询调整我的答案;如果您遇到困难,请发布一个问题。 @Clockwork-Muse 我发布了一个新问题,但没有人帮助我。我怎样才能做到这一点? ***.com/questions/23901430/… @traBolicEM - 因为您没有展示您面临的问题。您似乎希望我们为您做所有事情(甚至是微小的编辑)。您应该能够针对您的新问题调整我现有的答案;如果您的问题中有错别字,我很乐意调整我的答案,但如果您更改表格或其他列,我们希望 you 能够自行处理更改。如果我的回答对 this 问题的表现不够好,请在此处发布更多详细信息(例如您拥有的索引、EXPLAIN 计划的结果),以获得更好/额外的帮助。 【参考方案1】:

假设子查询的顺序假定是相同的(不是拼写错误),那么三者的组合就是greatest-n-per-group 问题。 mysql 有几个现有的解决方案,如果每组的行数很少,我想我更喜欢this answer 中的样式,对于您的情况,它看起来像这样:

SELECT rToUse.mr_id, rToUse.mesaj, rToUse.kayitZaman
FROM Mesajlarreply rToUse
LEFT JOIN Mesajlarreply rKnockout
       ON rKnockout.cId = rToUse.cId AND rToUse.mr_id < rKnockout.mr_id
WHERE rKnockout.mr_id IS NULL

(这是因为当连接行时,具有最大 mr_id 的行不匹配。由于 rKnockout.mr_id IS NULL 为真,所有其他行都将被丢弃) 您使用三个子查询的事实可能是在系统上做一个数字。

您的 CASE 语句计算为一组简单的布尔条件:

(M.k1 = '1' AND M.k2 = K.id) OR (M.k1 <> '1' AND M.k2 = '1' AND M.k1 = K.id)

...我将假设您不会遇到 both k1 k2 同时等于 '1' 的情况,因此M.k1 &lt;&gt; '1'(如果 is 为真,则保留CASE 的语义)将是不必要的。请注意,ORs 仍然可能是性能痛点。同样,如果这些列中的任何一个实际上是某种数字类型,则连接将首先需要转换,这可能会降低性能。

所以,我可能会从编写这样的查询开始:

SELECT K.id, M.m_id, K.sil, K.banli, K.foto, K.kullaniciAdi, K.ad, K.soyad, K.gender,
       R.mr_id, r.mesaj, r.kayitZaman
FROM Mesajlar M
JOIN Kullanicilar K
  ON  (M.k1 = '1' AND M.k2 = K.id) 
      OR (M.k2 = '1' AND M.k1 = K.id)
LEFT JOIN (SELECT rToUse.cId, rToUse.mr_id, rToUse.mesaj, rToUse.kayitZaman
           FROM Mesajlarreply rToUse
           LEFT JOIN Mesajlarreply rKnockout
                  ON rKnockout.cId = rToUse.cId 
                     AND rToUse.mr_id < rKnockout.mr_id
           WHERE rKnockout.mr_id IS NULL) R
       ON R.cId = M.m_id
ORDER BY M.m_id DESC
LIMIT 20

(当然,未经测试 - 未提供数据集) 我为子查询使用了LEFT JOIN 来保留给定的数据语义。如果您有所有案例的行(或只关心那些有的人),则可以将其更改为(INNER) JOIN。根据您的数据集的布局方式(以及您有/没有的索引),这可能不会表现得更好,但这通常对优化器应该更友好。

【讨论】:

以上是关于Mysql子查询优化?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:子查询检查超过 14000 行的子查询优化问题

MySQL 子查询优化 - where not in(子查询)

MySQL5.7性能优化系列——SQL语句优化——使用物化策略优化子查询

MySQL 查询优化 - 子查询 + 多连接

MySql学习 —— 数据库优化理论 —— 查询优化技术

Mysql优化系列之——优化器对子查询的处理