Как получить следующую / предыдущую запись в MySQL? - PullRequest
118 голосов
/ 19 сентября 2009

Допустим, у меня есть записи с идентификаторами 3, 4, 7, 9, и я хочу иметь возможность переходить от одного к другому с помощью навигации по следующим / предыдущим ссылкам. Проблема в том, что я не знаю, как получить запись с ближайшим более высоким ID.

Поэтому, когда у меня есть запись с идентификатором 4, мне нужно иметь возможность извлечь следующую существующую запись, которая будет равна 7. Запрос, вероятно, будет выглядеть примерно так:

SELECT * FROM foo WHERE id = 4 OFFSET 1

Как я могу получить следующую / предыдущую запись без извлечения всего набора результатов и итерации вручную?

Я использую MySQL 5.

Ответы [ 22 ]

231 голосов
/ 19 сентября 2009

следующая:

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)
132 голосов
/ 19 сентября 2009

В дополнение к решению Джемкалёнку :

следующая запись:

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

предыдущая запись:

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

edit: Поскольку этот ответ в последнее время получил несколько голосов, я действительно хочу подчеркнуть комментарий , который я сделал ранее, о понимании того, что столбец первичного ключа не предназначен столбец для сортировки, поскольку MySQL не гарантирует, что более высокие значения с автоматическим приращением обязательно будут добавлены позднее.

Если вас это не волнует, и вам просто нужна запись с более высоким (или более низким) id, этого будет достаточно. Только не используйте это как средство, чтобы определить, действительно ли запись добавлена ​​позже (или раньше). Вместо этого рассмотрите возможность использования столбца datetime для сортировки, например.

55 голосов
/ 14 апреля 2013

Все вышеперечисленные решения требуют двух вызовов базы данных. Приведенный ниже 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)
      )    
19 голосов
/ 19 сентября 2009
SELECT * FROM foo WHERE id>4 ORDER BY id LIMIT 1
11 голосов
/ 30 октября 2012

Я пытался сделать что-то похожее на это, но мне нужны были результаты, упорядоченные по дате, поскольку я не могу полагаться на поле идентификатора в качестве сортируемого столбца. Вот решение, которое я придумал.

Сначала мы узнаем индекс нужной записи в таблице, когда она сортируется так, как мы хотим:

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 и поместите его в оператор предела. Например, приведенное выше вернуло 21 для меня, поэтому я запускаю:

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

Предоставляет вам основную запись, а также следующую и предыдущую записи с указанным вами порядком.

Я пытался сделать это как один вызов базы данных, но не смог заставить оператор LIMIT принять переменную в качестве одного из параметров.

7 голосов
/ 07 мая 2015

Попробуйте этот пример.

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 , поскольку следующего значения нет.

7 голосов
/ 31 октября 2014

Используя подход @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)
5 голосов
/ 18 ноября 2013

Следующая строка

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 голосов
/ 15 декабря 2013

У меня была та же проблема, что и у Дэна, поэтому я использовал его ответ и улучшил его.

Сначала выберите индекс строки, здесь ничего не изменилось.

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 голосов
/ 17 ноября 2016

Это универсальное решение для условий с более одинаковыми результатами.

<?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
?>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...