【问题标题】:Optimize sub-query selecting last record of each group优化子查询选择每组的最后一条记录
【发布时间】:2012-06-24 18:00:39
【问题描述】:

我有这个查询,它是一个依赖查询并且需要很多执行时间

SELECT
  u.id,
  u.user_name,
  ifnull((select longitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
  ifnull((select latitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
  (select  created  from map  where user_id = 1  order by created desc   limit 1) as LatestTime
FROM users as u
WHERE id IN(SELECT
          user1_id FROM relation
        WHERE users.id = 1)
ORDER BY id;

我在(依赖)中尝试了这个查询

SELECT
  u.id,
  u.user_name,
  m.map_id,
  m.longitude,
  m.latitude,
  m.Date as created
FROM users as u
  left join (select
           map_id,
           longitude,
           latitude,
           user_id,
           max(created) as `Date`
         from map
         group by user_id) as m
    on m.user_id = u.id
WHERE id IN(SELECT
          user1_id FROM relation
        WHERE users.id = 1)
ORDER BY id;

问题是第一个查询是依赖的并且工作正常但需要很多执行时间。对于第二个查询,问题在于它没有获取最新创建的时间。 现在我想优化这个查询。主题是在子查询中我首先创建组,然后我试图获取每个组的最后一条记录。这是表格结构。

users : id , user_name
map   : map_id ,  user_id ,longitude , latitude, created 
relations : id , user1_id , user2_id , relation

【问题讨论】:

    标签: mysql optimization group-by subquery


    【解决方案1】:

    在需要性能的地方,SELECT 子句中的子查询确实很痛苦,必须被淘汰:)

    你可以重写这部分:

    SELECT
        u.id,
        u.user_name,
        ifnull((select longitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
        ifnull((select latitude from map where user_id = u.id order by map_id desc limit 1 ),0) as Longitude,
        (select  created  from map  where user_id = 1  order by created desc   limit 1) as LatestTime
    FROM users as u
    

    在:

    SELECT
        u.id,
        u.user_name,
        COALESCE(m1.longitude, 0) as longitude,
        COALESCE(m1.latitude, 0) as latitude
    FROM users u
    LEFT JOIN map m1 ON m1.user_id = u.id
    LEFT JOIN map m2 ON m2.user_id = m1.user_id AND m2.map_id > m1.map_id
    WHERE m2.map_id IS NULL
    

    我写了一个关于查询结构in this answer的简短说明。这是一个非常好的学习技巧,因为它更具可读性、更少的子查询和更明智的性能。

    我还没有查看IN 部分,但如果以上内容没有帮助,我会查看。

    Edit1:您可以提取创建日期并改用 MAX()。

    SELECT
        u.id,
        u.user_name,
        COALESCE(m1.longitude, 0) as longitude,
        COALESCE(m1.latitude, 0) as latitude,
        created.LatestTime
    FROM (SELECT MAX(created) FROM map WHERE user_id = 1) created
    INNER JOIN users u ON TRUE
    LEFT JOIN map m1 ON m1.user_id = u.id
    LEFT JOIN map m2 ON m2.user_id = m1.user_id AND m2.map_id > m1.map_id
    WHERE m2.map_id IS NULL
    

    【讨论】:

    • 感谢@Olivier,但创建的字段是最新记录未获取的主要问题
    • 我没有添加这个字段,因为我没有看到它可能是一个问题。它不依赖于当前选择的行,因此您可以将其放在 FROM 子句中。请参阅我的 Edit1。
    • 本书SQL Antipatterns 专门有一章介绍这种类型的解决方案。我就是这么学的。
    • 非常感谢@marcus,我有这本书我正在阅读,看看问题是如何解决的
    • 嗯@Olivier 很好,对您的查询稍作修改,我已经让它工作了
    猜你喜欢
    • 1970-01-01
    • 2013-03-05
    • 2015-07-24
    • 2011-10-20
    • 2021-10-27
    • 1970-01-01
    • 2021-01-13
    相关资源
    最近更新 更多