从MySQL中间检索第一个,最后一个和3个记录的最有效方法是什么?

时间:2011-07-18 21:12:42

标签: mysql optimization

背景

我有一个显示网络通讯的网页。目前有1622页。我需要显示当前的漫画页面(我有它的ID),以及指向第一页,上一页,下一页和最后一页的链接。排序也是非常重要的(由于奇怪的数据库设计有很长的ORDER BY - 传统的东西),所以我不能做“ID = 1”之类的东西。

问题

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

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

已添加:一种解决方案是执行多个查询 - 每个查询需要一个查询。我应该说我知道它并想到更优雅的东西。

3 个答案:

答案 0 :(得分:5)

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

如果不能修改现有表,您可以创建一个只有两列的新表,一个指向您的页表的FK,以及一个如上所述的OrderedID列?

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

答案 1 :(得分:0)

嗯..我认为这个问题可以用很少的逻辑来完成。如果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应该是什么的原因是你可以在这里和那里更新低,以使“旧”漫画......好吧......不那么老(如果这是有道理的)。因此,不是从0开始(有史以来第一个漫画),你可以从50,100,1000开始,等等。 :)



好的所以现在因为有这条信息(并不是所有的id都存在(0-1600)而且其中一些不相关)。

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

1:创建某种链接表,无论如何都要调用它,[相关通讯]有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);

只要漫画插入正确的顺序,这应该适合你!为什么这样做是因为auto字段只是放入一个单独的表中,然后从那里检索和连接。这样就不必更改现有数据,除非插入新漫画时,相关表格也必须更新。

答案 2 :(得分:0)

只有1600行,我认为根本没有任何效率问题,但是你实现了这一点。但我们假设您可能有16M行。

假设您的查询类似于:

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
  )