如何获取MySQL中的下一个/上一个记录?


129

假设我有ID为3、4、7、9的记录,并且我希望能够通过下一个/上一个链接导航到另一个。问题是,我不知道如何获取具有最近的较高ID的记录。

因此,当我有一个ID为4的记录时,我需要能够获取下一个现有记录,即7。查询可能看起来像

SELECT * FROM foo WHERE id = 4 OFFSET 1

如何获取下一个/上一个记录,而不获取整个结果集并手动进行迭代?

我正在使用MySQL 5。


8
您不应该依赖ID进行排序(也就是说:假定您的ID是一个auto_increment列)。您应该在表中创建另一列,以明确描述排序顺序。
体面的摔跤手

Answers:


258

下一个:

select * from foo where id = (select min(id) from foo where id > 4)

以前:

select * from foo where id = (select max(id) from foo where id < 4)

7
我认为子查询应该是(从id> 4的foo中选择min(id)和(从id <4的foo中选择max(id))。
体面的摔跤手

1
您是对的,我忘记了FROM子句。感谢您指出。:)
longneck

2
这不是解决方案,因为如果从“ foo”中删除行,将会发生什么?:P
Valentin Hristov 2014年

@valentinhristov我不明白你在说什么。请提供一个例子。
longneck 2014年

@longneck如果您在录制后发布,一切都很好。但是,如果您在版本ID之后发布内容,则不是按顺序排列的条件。
Valentin Hristov 2014年

140

除了cemkalyoncu的解决方案:

下一条记录:

SELECT * FROM foo WHERE id > 4 ORDER BY id LIMIT 1;

之前的纪录:

SELECT * FROM foo WHERE id < 4 ORDER BY id DESC LIMIT 1;

编辑:由于该答案最近得到了一些好评,所以我真的想强调一下早些时候发表的有关理解主键列不作为要排序的列的评论,因为MySQL不保证更高的自动递增,则必须在以后添加值。

如果您对此并不在意,只需要更高(或更低)的记录id就可以了。只是不要使用此方法来确定是否实际上在以后(或更早)添加记录。相反,请考虑例如使用datetime列进行排序。


我更喜欢这个答案,我排序的时间戳,这意味着没有子查询,所以2个查询总量,而不是4
莫里斯

61

以上所有解决方案都需要两个数据库调用。下面的sql代码将两个sql语句组合为一个。

select * from foo 
where ( 
        id = IFNULL((select min(id) from foo where id > 4),0) 
        or  id = IFNULL((select max(id) from foo where id < 4),0)
      )    

3
绝对是最佳解决方案。快速且一站式查询。
守护程序

即使在更复杂的查询中也能做到完美!
taiar 2014年

DISTINCT之前添加*将使它变得更好,也就是说,当然id可以具有0价值。
Ejaz

优秀的解决方案。在第一个和最后一个记录上将返回单个id而不是两个。
lubosdz


12

我试图做类似的事情,但是我需要按日期排序的结果,因为我不能依赖ID字段作为可排序的列。这是我想出的解决方案。

首先,根据需要对表中所需记录的索引进行排序时,我们会找到它们的索引:

SELECT row
FROM 
(SELECT @rownum:=@rownum+1 row, a.* 
FROM articles a, (SELECT @rownum:=0) r
ORDER BY date, id) as article_with_rows
WHERE id = 50;

然后将结果减2,将其放入limit语句中。例如,上面为我返回了21,所以我运行:

SELECT * 
FROM articles
ORDER BY date, id
LIMIT 19, 3

给定您的主要记录,以及指定顺序的下一个和上一个记录。

我试图将其作为单个数据库调用来完成,但是无法获得LIMIT语句来将变量作为其参数之一。


我对您的方法感兴趣。如果您还需要加入JOIN,该怎么办?说,从文章开始,JOIN作者b在b.this = a.that上。在我看来,JOIN弄乱了顺序。这是一个示例pastebin.com/s7R62GYV
idrarig 2013年

1
要进行JOIN,您需要定义两个不同的@variables。`SELECT current.row,current.id,previous.row,previous.id FROM(SELECT @rownum:= @ rownum + 1行,a。* FROM文章a,(SELECT @rownum:= 0)r按日期排序, id)作为current_row左联接(SELECT @ rownum2:= @ rownum2 + 1行,a。*从文章a,(SELECT @ rownum2:= 0)r ORDER BY date,id)作为previous_row ON(current_row.id = previous_row。 id)AND(current_row.row = previous_row.row -1)`
Eduardo Moralles 2014年

7

试试这个例子。

create table student(id int, name varchar(30), age int);

insert into student values
(1 ,'Ranga', 27),
(2 ,'Reddy', 26),
(3 ,'Vasu',  50),
(5 ,'Manoj', 10),
(6 ,'Raja',  52),
(7 ,'Vinod', 27);

SELECT name,
       (SELECT name FROM student s1
        WHERE s1.id < s.id
        ORDER BY id DESC LIMIT 1) as previous_name,
       (SELECT name FROM student s2
        WHERE s2.id > s.id
        ORDER BY id ASC LIMIT 1) as next_name
FROM student s
    WHERE id = 7; 

注意:如果未找到值,则它将返回null

在上面的示例中,“ 上一个”值为“ Raja”Next值将为null,因为没有下一个值。


7

使用@Dan的方法,可以创建JOIN。只需对每个子查询使用不同的@variable。

SELECT current_row.row, current_row.id, previous_row.row, previous_row.id
FROM (
  SELECT @rownum:=@rownum+1 row, a.* 
  FROM articles a, (SELECT @rownum:=0) r
  ORDER BY date, id
) as current_row
LEFT JOIN (
  SELECT @rownum2:=@rownum2+1 row, a.* 
  FROM articles a, (SELECT @rownum2:=0) r
  ORDER BY date, id
) as previous_row ON
  (current_row.id = previous_row.id) AND (current_row.row = previous_row.row - 1)

正是我需要的,谢谢。请注意,您的选择不正确current-- current_row&&previous-> previous_row
Ludovic Guillaume 2014年

此查询对于查找是否已从表中删除记录(auto_increment id列的顺序中断)是否有用。
Dharmang '18 -4-25

@LudovicGuillaume您能更具体一点吗?我不理解你的话,对不起。但是该查询在previous_row中没有任何元素。*
Walter Schrabmair,

4

下一行

SELECT * FROM `foo` LIMIT number++ , 1

上一行

SELECT * FROM `foo` LIMIT number-- , 1

采样下一行

SELECT * FROM `foo` LIMIT 1 , 1
SELECT * FROM `foo` LIMIT 2 , 1
SELECT * FROM `foo` LIMIT 3 , 1

采样上一行

SELECT * FROM `foo` LIMIT -1 , 1
SELECT * FROM `foo` LIMIT -2 , 1
SELECT * FROM `foo` LIMIT -3 , 1

SELECT * FROM `foo` LIMIT 3 , 1
SELECT * FROM `foo` LIMIT 2 , 1
SELECT * FROM `foo` LIMIT 1 , 1

4

我遇到了与Dan相同的问题,因此我使用了他的答案并对它进行了改进。

首先选择行索引,这里没有什么不同。

SELECT row
FROM 
(SELECT @rownum:=@rownum+1 row, a.* 
FROM articles a, (SELECT @rownum:=0) r
ORDER BY date, id) as article_with_rows
WHERE id = 50;

现在使用两个单独的查询。例如,如果行索引为21,则选择下一条记录的查询将为:

SELECT * 
FROM articles
ORDER BY date, id
LIMIT 21, 1

要选择上一条记录,请使用以下查询:

SELECT * 
FROM articles
ORDER BY date, id
LIMIT 19, 1

请记住,对于第一行(行索引为1),限制将变为-1,并且您将收到MySQL错误。您可以使用if语句来防止这种情况。只是不要选择任何内容,因为无论如何都没有以前的记录。在最后一条记录的情况下,将存在下一行,因此将没有结果。

还请记住,如果使用DESC而不是ASC进行排序,则查询以选择下一行和上一行仍然相同,但已切换。


3

这是具有更多相同结果的条件的通用解决方案。

<?php
$your_name1_finded="somethnig searched"; //$your_name1_finded must be finded in previous select

$result = db_query("SELECT your_name1 FROM your_table WHERE your_name=your_condition ORDER BY your_name1, your_name2"); //Get all our ids

$i=0;
while($row = db_fetch_assoc($result)) { //Loop through our rows
    $i++;
    $current_row[$i]=$row['your_name1'];// field with results
    if($row['your_name1'] == $your_name1_finded) {//If we haven't hit our current row yet
        $yid=$i;
    }
}
//buttons
if ($current_row[$yid-1]) $out_button.= "<a  class='button' href='/$your_url/".$current_row[$yid-1]."'>BUTTON_PREVIOUS</a>";
if ($current_row[$yid+1]) $out_button.= "<a  class='button' href='/$your_url/".$current_row[$yid+1]."'>BUTTON_NEXT</a>";

echo $out_button;//display buttons
?>

3

在这里,我们有一种使用单个MySQL查询来获取上一条和下一条记录的方法。其中5是当前记录的ID。

select * from story where catagory=100 and  (
    id =(select max(id) from story where id < 5 and catagory=100 and order by created_at desc) 
    OR 
    id=(select min(id) from story where id > 5 and catagory=100 order by created_at desc) )

2

如何获取MySQL和PHP中的下一个/上一个记录?

我的示例是仅获取ID

function btn_prev(){

  $id = $_POST['ids'];
  $re = mysql_query("SELECT * FROM table_name WHERE your_id < '$id'  ORDER BY your_id DESC LIMIT 1");

  if(mysql_num_rows($re) == 1)
  {
    $r = mysql_fetch_array($re);
    $ids = $r['your_id'];
    if($ids == "" || $ids == 0)
    {
        echo 0;
    }
    else
    {
        echo $ids;
    }
  }
  else
  {
    echo 0;
  }
}



function btn_next(){

  $id = $_POST['ids'];
  $re = mysql_query("SELECT * FROM table_name WHERE your_id > '$id'  ORDER BY your_id ASC LIMIT 1");

  if(mysql_num_rows($re) == 1)
  {
    $r = mysql_fetch_array($re);
    $ids = $r['your_id'];
    if($ids == "" || $ids == 0)
    {
        echo 0;
    }
    else
    {
        echo $ids;
    }
  }
  else
  {
    echo 0;
  }
}

拒绝投票,因为它容易受到SQL Injection的攻击。我知道这是一篇旧文章,但是新手开发人员应该意识到这一点。
ayrtonvwf

2

优化@Don方法以仅使用一个查询

SELECT * from (
  SELECT 
     @rownum:=@rownum+1 row,
     CASE a.id WHEN 'CurrentArticleID' THEN @currentrow:=@rownum ELSE NULL END as 'current_row',
     a.*  
  FROM articles a,
     (SELECT @currentrow:=0) c,  
     (SELECT @rownum:=0) r
   ORDER BY `date`, id  DESC
 ) as article_with_row
 where row > @currentrow - 2
 limit 3

用当前文章ID更改CurrentArticleID

SELECT * from (
  SELECT 
     @rownum:=@rownum+1 row,
     CASE a.id WHEN '100' THEN @currentrow:=@rownum ELSE NULL END as 'current_row',
     a.*  
  FROM articles a,
     (SELECT @currentrow:=0) c,  
     (SELECT @rownum:=0) r
   ORDER BY `date`, id  DESC
 ) as article_with_row
 where row > @currentrow - 2
 limit 3

这是到目前为止我在这里找到的最有用的答案。我建议的唯一真正的改进就是使用变量,并将所有唯一的部分移到顶部,以便于编辑。然后可以轻松地使其成为经常引用的功能或过程。
liljoshu

1

您还可以使用另一种技巧来显示上一行的列,使用所需的任何顺序,并使用类似于@row技巧的变量:

SELECT @prev_col_a, @prev_col_b, @prev_col_c,
   @prev_col_a := col_a AS col_a,
   @prev_col_b := col_b AS col_b,
   @prev_col_c := col_c AS col_c
FROM table, (SELECT @prev_col_a := NULL, @prev_col_b := NULL, @prev_col_c := NULL) prv
ORDER BY whatever

显然,按顺序评估选择列,因此这将首先选择已保存的变量,然后将变量更新到新行(在过程中选择它们)。

注意:我不确定这是否是定义的行为,但是我已经使用了它并且可以工作。


1

如果您想为查询提供多个信息id并获取next_id所有信息...

cur_id在您的选择字段中分配,然后将其提供给子查询并进入next_id选择字段。然后选择next_id

使用longneck答案calc next_id

select next_id
from (
    select id as cur_id, (select min(id) from `foo` where id>cur_id) as next_id 
    from `foo` 
) as tmp
where next_id is not null;

1
CREATE PROCEDURE `pobierz_posty`(IN iduser bigint(20), IN size int, IN page int)
BEGIN
 DECLARE start_element int DEFAULT 0;
 SET start_element:= size * page;
 SELECT DISTINCT * FROM post WHERE id_users .... 
 ORDER BY data_postu DESC LIMIT size OFFSET start_element
END

6
欢迎使用StackOverflow。完全由代码组成的答案很少有用。请考虑提供一些有关代码执行情况的解释。用英语编写代码文档和/或变量名也将有所帮助。
Politank-Z 2015年

1

如果您有一个索引列,例如id,则可以在一个sql请求中返回上一个和下一个id。用您的值替换:id

SELECT
 IFNULL((SELECT id FROM table WHERE id < :id ORDER BY id DESC LIMIT 1),0) as previous,
 IFNULL((SELECT id FROM table WHERE id > :id ORDER BY id ASC LIMIT 1),0) as next

0

如果我排在最后,反之亦然,获得下一个和预览记录的解决方案也可以返回到第一条记录

我没有使用ID,而是将标题用于了不错的网址

我正在使用Codeigniter HMVC

$id = $this->_get_id_from_url($url);

//get the next id
$next_sql = $this->_custom_query("select * from projects where id = (select min(id) from projects where id > $id)");
foreach ($next_sql->result() as $row) {
    $next_id = $row->url;
}

if (!empty($next_id)) {
    $next_id = $next_id;
} else {
    $first_id = $this->_custom_query("select * from projects where id = (SELECT MIN(id) FROM projects)");
    foreach ($first_id->result() as $row) {
        $next_id = $row->url;
    }
}

//get the prev id
$prev_sql = $this->_custom_query("select * from projects where id = (select max(id) from projects where id < $id)");
foreach ($prev_sql->result() as $row) {
    $prev_id = $row->url;
}

if (!empty($prev_id)) {
    $prev_id = $prev_id;
} else {
    $last_id = $this->_custom_query("select * from projects where id = (SELECT MAX(id) FROM projects)");
    foreach ($last_id->result() as $row) {
        $prev_id = $row->url;
    }     
}

拒绝投票,因为它容易受到SQL Injection的攻击。我知道这是一篇旧文章,但是新手开发人员应该意识到这一点。
ayrtonvwf

0

这是我的答案。我从“ Decent Dabbler ”中提出了一个想法,并添加了检查id是否在min(id)和max(id)之间的部分。这是创建表的部分。

CREATE TABLE Users (
UserID int NOT NULL auto_increment,
UserName varchar(45),
UserNameID varchar(45),
PRIMARY KEY (UserID)

);

下一步是创建一个存储过程,该过程负责获取先前的ID。

    CREATE DEFINER=`root`@`localhost` PROCEDURE `printPreviousIDbySelectedIDUser`(
IN ID int,
IN search_name varchar(45)
)
BEGIN
SELECT CONCAT(ns.UserID) AS 'Previous ID' from Users ns
 where ns.UserName=search_name AND ns.UserID IN (select min(ns.UserID) from Users ns where ns.UserID > ID 
 union select max(ns.UserID) from Users ns where  ns.UserID < ID) LIMIT 1 ;
END

如果索引是排序的,则第一种方法是好的,但如果索引没有排序,则第一种方法是好的。例如,如果您有索引:1、2、7,则需要以这种方式获得更好的索引号2,以使用另一种方法。

    CREATE DEFINER=`root`@`localhost` PROCEDURE `getPreviousUserID`(
IN ID int,
IN search_name varchar(45)
)
BEGIN
SELECT CONCAT(ns.UserID) AS 'Previous ID' from Users ns
  WHERE ns.UserName=search_name AND  ns.UserID < ID   ORDER BY ns.UserID DESC LIMIT 1;
END

1
寻找死灵法师徽章?这个问题已经被接受了十年的答案。
雅各布·阿诺德

我刚刚找到了这个主题,因为最近遇到了同样的问题。
丹尼斯,

0

100%工作

SELECT * FROM `table` where table_id < 3 ORDER BY `table_id` DESC limit 1

-1

我认为要在SQL表中具有真实的下一行或上一行,我们需要等于(<或>)的实际值,如果需要更改排序表中行的位置,则返回多个。

我们需要该值$position来搜索neighbours行。在我的表格中,我创建了列“ position”

用于获取所需行的SQL查询是:

接下来:

SELECT * 
FROM `my_table` 
WHERE id = (SELECT (id) 
            FROM my_table 
            WHERE position = ($position+1)) 
LIMIT 1

对于以前的:

 SELECT * 
 FROM my_table 
 WHERE id = (SELECT (id) 
             FROM my_table 
             WHERE `position` = ($position-1)) 
 LIMIT 1

如果已删除一行,则此行将中断。例如:如果ID是数字1,2,6,7,8。
凯文·沃特森

-2

从foo中选择前1 * *,其中id> 4按ID asc排序


1
等效的MySQL是“ LIMIT 1”: select * from foo where id>4 order by id asc LIMIT 1
暴民
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.