【问题标题】:Slow query with Having on calculate field具有计算字段的慢查询
【发布时间】:2018-03-02 15:56:54
【问题描述】:

我有一个很慢的查询...我想显示我附近的最后 12 个最新成员(靠近登录用户),我的开发数据库有 150k 行。

花了 1 秒多的时间,解释查询告诉我过滤了 30k 行 所以在我的开发数据库中过滤了 150k 行 30k...我的在线服务器比这个大得多....

这是我的查询:

SELECT  profils.*,
        Users.username,
        ( SELECT  count(*)
                from  profilsphotos pp
            where  pp.iduser=Profils.iduser
        ) as nbpics,
        ATAN2(SQRT(POW(COS(RADIANS(50.78961000)) * SIN(RADIANS(Y(gm_coor) - 4.64956000)),
                        2) + POW(COS(RADIANS(X(gm_coor))) * SIN(RADIANS(50.78961000)) - SIN(RADIANS(X(gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(gm_coor) - 4.64956000)),
                                                2)), (SIN(RADIANS(X(gm_coor))) * SIN(RADIANS(50.78961000)) + COS(RADIANS(X(gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(gm_coor) - 4.64956000)))
             ) * 6372.795 AS distance
    from  Users
    inner join  Profils  ON Users.id=Profils.iduser
    where  Profils.Actif=1
      and  profils.idsexe=2
      and  profils.idlookingfor=1
      and  Profils.iduser<>1
    HAVING  distance<400
    order by  Users.id desc, distance asc
    limit  12 

请注意,我在这四个字段上添加了索引:actif、idsexe、idlookingfor 和 iduser

我的查询有什么问题?

非常感谢!

帕斯卡

【问题讨论】:

  • 你能粘贴解释输出以及这个查询的结果吗:select count(*) from Users inner join Profils on Users.id=Profils.iduser where Profils.Actif = 1 and profils. idsexe = 2 和 profils.idlookingfor = 1 和 Profils.iduser 1
  • 你好,马克,同样的事情...... 30k 过滤......

标签: mysql performance optimization having


【解决方案1】:

我会将 SELECT 子句中的子查询提取到一个临时表中,对其进行索引并加入其中,而不是为 select 子句中的每条记录执行它(30K 次)。

所以步骤是:创建一个临时表,对其进行索引,运行优化查询。

首先,为查询创建相关索引:

ALTER TABLE
  `Profils`
ADD
  INDEX `profils_idx_actif_iduser` (`Actif`, `iduser`);

ALTER TABLE
  `Users`
ADD
  INDEX `users_idx_id_username` (`id`, `username`);

ALTER TABLE
  `profils`
ADD
  INDEX `profils_idx_idsexe_idlookingfor` (`idsexe`, `idlookingfor`);

ALTER TABLE
  `profilsphotos`
ADD
  INDEX `profilsphotos_idx_iduser` (`iduser`);

现在,创建临时表并为其编制索引:

-- Transformed subquery to a temp table to improve performance
CREATE TEMPORARY TABLE IF NOT EXISTS temp1 AS SELECT
        count(*) AS nbpics,
        iduser 
    FROM
        profilsphotos pp 
    WHERE
        1 = 1 
    GROUP BY
        iduser 
    ORDER BY
        NULL;

ALTER TABLE
  `temp1`
ADD
  INDEX `temp1_idx_iduser_nbpics` (`iduser`, `nbpics`);

现在尝试运行这个查询而不是原来的查询,看看它是否运行得更快:

SELECT
        optimizedSub1.*,
        temp1.nbpics 
    FROM
        (SELECT
            Users.username,
            ATAN2(SQRT(POW(COS(RADIANS(50.78961000)) * SIN(RADIANS(Y(Profils.gm_coor) - 4.64956000)),
            2) + POW(COS(RADIANS(X(Profils.gm_coor))) * SIN(RADIANS(50.78961000)) - SIN(RADIANS(X(Profils.gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(Profils.gm_coor) - 4.64956000)),
            2)),
            (SIN(RADIANS(X(Profils.gm_coor))) * SIN(RADIANS(50.78961000)) + COS(RADIANS(X(Profils.gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(Profils.gm_coor) - 4.64956000)))) * 6372.795 AS distance 
        FROM
            Users 
        INNER JOIN
            Profils 
                ON Users.id = Profils.iduser 
        WHERE
            Profils.Actif = 1 
            AND profils.idsexe = 2 
            AND profils.idlookingfor = 1 
            AND Profils.iduser <> 1 
        HAVING
            distance < 400 
        ORDER BY
            Users.id DESC,
            distance ASC LIMIT 12) AS optimizedSub1 
    LEFT JOIN
        temp1 
            ON temp1.iduser = optimizedSub1.iduser

【讨论】:

  • 你好汤姆,谢谢!它是一样的...... 30k 行......另外......这是正常的......我应该总是重新创建临时表吗?因为,如果我复制并粘贴您的最后一个查询... MySQL 说 temp1 不存在!感谢您的帮助!
  • @pascal22 - 临时表仅可用于该特定会话,因此每次都应创建并索引它,就像运行查询一样。执行时间总体上是否有所改善?
  • 好的,我明白了...感谢您的解释。时间有点慢。:旧查询是 0.2619 秒,您的查询是 0.1734 创建临时表 + 0.1920 秒用于选择查询...
【解决方案2】:

Profils需要

INDEX(Actif, idsexe, idlookingfor) -- in any order

也许distance应该是第一个?..

order by  Users.id desc, distance asc

Y(gm_coor) 是什么?如果是一个存储函数,我们需要了解更多。哪个表有gm_coor?在那之后,也许我们可以讨论一个“边界框”作为部分加速。

创建另一个SELECTs 的嵌套并将nbpics 的计算移到它上面。目前,COUNT(*) 正在执行 30K 次。改完之后就只有12次了。

重新制定

SELECT  p2.*,
        u.username, 
        ( SELECT  COUNT(*)
            FROM  profilsphotos pp
            where  pp.iduser = p2.iduser 
        ) as nbpics,
        x.distance
    FROM  
        ( SELECT  p1.id,    -- assuming this the PK of Profils
                  (...) AS distance
            FROM  Profils AS p1
            WHERE  p1.Actif=1
              and  p1.idsexe=2
              and  p1.idlookingfor=1
              and  p1.iduser<>1
            HAVING  distance < 400
            ORDER BY  distance
            LIMIT  12 
        ) AS x
    JOIN  profils AS p2 USING(id)
    JOIN  Users AS u  ON u.id = p2.iduser;

【讨论】:

  • 你好瑞克!同样的事情......超过30k行......我添加了actif,idsexe,idlooking for作为索引......我将顺序更改为:order by disrance asc,users.id desc ..还是一样......
  • 我删除了所有...距离字段、距离顺序、users.id 顺序、nbpics 字段。这是我没有 order by 的 where 子句: where Profils.Actif=1 and profils.idsexe=2 and profils.edlookingfor=1 limit 12.. and still 30k rows ???为什么?
  • 也许你没明白我的意思。请参阅我的重新制定。
  • 你好瑞克!对不起!我不太明白...所以我尝试了您对我的建议,但它仍然是 30k 行...但我看到 nbpics 有 12 行...所以问题...可能是索引?我可以强制一个特定的索引....请注意,我有很多不同的索引用于此表...非常感谢您的帮助!
  • @pascal22 - 嗯...如果您提供 SHOW CREATE TABLEEXPLAIN SELECT ... 可能会有所帮助;其中可能有一些线索。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 2018-08-08
  • 2017-04-28
  • 1970-01-01
相关资源
最近更新 更多