【问题标题】:What is the most efficient way to retrieve the first, last and 3 records from the middle in MySQL?在 MySQL 中从中间检索第一条、最后一条和 3 条记录的最有效方法是什么?
【发布时间】:2011-07-20 23:11:41
【问题描述】:

背景

我有一个显示网络漫画的网页。目前有 1622 页。我需要显示当前漫画页面(我有它的 ID),以及指向第一页、上一页、下一页和最后一页的链接。排序也很重要(由于古怪的数据库设计 - 遗留的东西,有一个很长的 ORDER BY),所以我也不能做像“where ID=1”这样的事情。

问题

所以,问题很简单 - 我知道记录的 ID。我执行SELECT ... FROM ... WHERE ... ORDER BY ... 查询,想要检索第一条记录、最后一条记录、具有我知道的 ID 的记录,以及直接在具有已知 ID 的记录之前和之后的记录。

未过滤的查询返回超过 1600 行,并且每天都有一个新行。查询将每秒运行几次(有一个公平的读者群)。最有效的方法是什么?有什么比天真的“获取所有行并过滤掉我在 PHP 代码中需要的内容”更好的方法?请注意,我知道我可以在 PHP 端缓存结果,但我想知道这里是否有一些与 MySQL 相关的优化。

添加:一种解决方案是执行多个查询 - 每个所需值一个。我应该说我知道它并且正在考虑更优雅的东西。

【问题讨论】:

    标签: mysql optimization


    【解决方案1】:

    为什么不添加一个有序且以正确方式排序的 OrderedID 列?您最初可以使用复杂查询进行填充,然后在添加新页面时保持更新。

    如果修改现有表不是一个选项,您可以创建一个只有两列的新表,一个指向您的 pages 表的 FK 和一个像上面一样的 OrderedID 列?

    这意味着对于任何 ID=X 的页面,您都需要 1、X-1、X、X+1 和 Max() - 只有 Max() 实际上需要查询,并且每天只会更改一次。其他的可以计算出来。

    【讨论】:

      【解决方案2】:

      只有 1600 行,我认为根本不存在任何效率问题,但是您实现了这一点。不过,让我们假设您可能有 1600 万行。

      假设您的查询类似于:

      SELECT ...  FROM ...  WHERE ... 
      ORDER BY colA ASC
             , colB DESC
             , ...
             , colZ ASC
      

      id 是唯一键,而具体的id@id

      您可以在(colA, colB, ..., colZ) 上添加索引并尝试以下操作:

        ( SELECT ...  FROM ...  WHERE ... 
          ORDER BY colA ASC
                 , colB DESC
                 , ...
                 , colZ ASC
          LIMIT 1          --- to get the first row
        )
        UNION ALL
        ( SELECT ...  FROM ...  WHERE ... 
                                  AND (colA, colB, ..., colZ) 
                <  ( SELECT colA, colB, ..., colZ
                     FROM ... 
                     WHERE id = @id )
          ORDER BY colA DESC       --- order reversed
                 , colB ASC        --- order reversed
                 , ...
                 , colZ DESC       --- order reversed
          LIMIT 1          --- to get the previous row
        )
        UNION ALL
        ( SELECT ...  FROM ...  WHERE ... 
                                  AND (colA, colB, ..., colZ) 
                >= ( SELECT colA, colB, ..., colZ
                     FROM ... 
                     WHERE id = @id )
          ORDER BY colA ASC
                 , colB DESC
                 , ...
                 , colZ ASC
          LIMIT 2          --- to get the row with @id and the next one
        )
        UNION ALL
        ( SELECT ...  FROM ...  WHERE ... 
          ORDER BY colA DESC       --- order reversed
                 , colB ASC        --- order reversed
                 , ...             --- ...
                 , colZ DESC       --- order reversed
          LIMIT 1          --- to get the last row
        )
      

      【讨论】:

      • 又是多查询方案。好的,我想它应该因为话题而受到支持。我仍然希望有更好的东西。
      • @Vilx-:这不是一个多查询解决方案。这是一个包含多个子查询并使用UNION ALL 的查询。我什至会在一百万行表中尝试它。
      • UNION ALL 的性能提升真的比只运行 4 个单独的查询好得多吗?
      • 不,UNION 或 UNION ALL 的速度几乎相同,只返回 5 行。
      • 但是运行 4 或 5 个单独的查询,而不是 1 个,为什么?
      【解决方案3】:

      嗯.. 我认为这个问题可以用更少的逻辑来解决。如果 ID 是自动递增的(这会使这变得超级简单),那么您真的可以使用基本的算术。

      SELECT count(comicId)
      FROM comics;
      
      //Get that answer in php
      $low = 0; //this could be anything. 
      $high = count;
      $one = (($high - $low) / 2) + ($low - 1);
      $two = $one + 1;
      $three = $two + 1;
      
      SELECT *
      FROM comics
      WHERE comicId IN ($low, $high, $one, $two, $three);
      

      $low 应该是任何东西的原因是您可以在这里和那里更新 low 以使“旧”漫画......嗯......不那么旧(如果这有意义的话)。因此,您可以从 50、100、1000 等等开始,而不是从 0(有史以来的第一部漫画)开始。 :)



      好吧,既然有这条信息(不是所有的 id 都存在 (0-1600),其中一些是不相关的)。

      请记住,这不是最终解决方案,我只是在编造一个。编程时有很多选择。牢记效率(如果需要)。

      1:创建某种链接表,随便命名,[relevantcomics] 有 2 个字段,所需漫画的 id 和一个自动递增字段。

      2:做一些与上面相同的逻辑,除了相应地修改。

      $low = 0; //this could be anything. 
      $high = count;
      $one = (($high - $low) / 2) + ($low - 1);
      $two = $one + 1;
      $three = $two + 1;
      
      SELECT C.*
      FROM comics AS C
          JOIN relevantcomics AS RC
            ON RC.id = C.comicid
      WHERE RC.autoId IN ($low, $high, $one, $two, $three);
      

      只要以正确的顺序插入漫画,这应该适合您!这样做的原因是,然后将自动字段放入单独的表中,然后从那里检索并加入。这样您现有的数据就不必更改,除了插入新漫画时,相关表也必须更新。

      【讨论】:

      • 不,ID 不是自动递增的。或者更确切地说,它们是,但页面有时也会被删除,并且该表中还有来自其他漫画的其他页面,它们不相关。
      • @Jason 的回答将是最好的选择。创建一个外键表来检索正确的漫画。我会用“答案”更新我的答案(它仍然可以以不同的方式完成)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-28
      • 1970-01-01
      • 2019-03-28
      • 1970-01-01
      • 2010-11-21
      相关资源
      最近更新 更多