Як отримати наступний / попередній запис у MySQL?


129

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

Отже, коли у мене є запис з ідентифікатором 4, мені потрібно мати можливість отримати наступний існуючий запис, який би був 7. Запит, ймовірно, виглядатиме як

SELECT * FROM foo WHERE id = 4 OFFSET 1

Як я можу отримати наступний / попередній запис без отримання всього набору результатів і вручну повторення?

Я використовую MySQL 5.


8
Не слід покладатися на ідентифікаційні дані для сортування (тобто: припускаючи, що ваш ідентифікатор - це стовпець з автоматичним збільшенням). Вам слід створити інший стовпець у таблиці, який чітко зображує порядок сортування.
Гідний даблер

Відповіді:


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
Я думаю, що підзапити повинні бути (виберіть min (id) від foo, де id> 4) і (select max (id) від foo, де id <4).
Гідний даблер

1
ти маєш рацію, я забув пункт "ВІД" дякую, що вказали на це. :)
longneck

2
Це не є рішенням, оскільки якщо ви видалите рядок із "foo", що буде? : P
Валентин Христов

@valentinhristov Я не розумію, що ти говориш. Наведіть приклад.
longneck

@longneck Якщо ви публікуєте повідомлення після запису, все добре. Але якщо ви публікуєте вміст після версії ідентифікатора версії, це не критерій розташування в послідовності.
Валентин Христов

140

Крім розчину цемкаліонку :

наступний запис:

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цього буде достатньо. Просто не використовуйте це як засіб для визначення того, чи є запис насправді доданий пізніше (або раніше). Замість цього, розгляньте можливість використання стовпця з датою для сортування, наприклад.


Я дуже віддаю перевагу цій відповіді, я сортую за часовою міткою, і це означає, що немає підзапитів, тому всього 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

Додавання DISTINCTраніше *зробить це ще краще, тобто, звичайно, якщо воно idможе мати 0як значення.
Еджаз

Відмінне рішення. На першому та останньому записі повернеться один, idа не два.
lubosdz

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

1
+1 Я думаю, що це найчистіше рішення. Але пункт LIMIT має бути останнім: ВИБІРТИ * ВІД foo WHERE id> 4 ORDER BY id LIMIT 1
Decent Dabbler

а SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1для попереднього запису
fwonce

12

Я намагався зробити щось подібне до цього, але мені були потрібні результати, упорядковані за датою, оскільки я не можу покластися на поле ідентифікатора як на сортувальний стовпчик. Ось таке рішення я придумав.

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

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, щоб прийняти змінну як один із її параметрів.


Мене цікавить ваш підхід. Що робити, якщо вам також потрібно буде ПРИЄДНАЙТЕСЬ? Скажіть, З статей ПРИЄДНАЙТЕСЬ автори b ON b.this = a.that. Мені здається, що ПРИЄДНАЙТЕ псує замовлення. Ось приклад pastebin.com/s7R62GYV
idrarig

1
Щоб приєднатись до приєднання, вам потрібно визначити дві різні @variables. `SELECT current.row, current.id, previous.row, previous.id ВІД (SELECT @rownum: = @ rownum + 1 рядок, a. * ІЗ статей a, (SELECT @rownum: = 0) r ЗАМОВИТИ ЗА датою, id) як current_row НАЛЕЖЕНО ПРИЄДНАЙТЕСЬ (SELECT @ rownum2: = @ rownum2 + 1 рядок, a. * ІЗ статей a, (SELECT @ rownum2: = 0) r ЗАМОВИТИ за датою, id) як previous_row ON (current_row.id = previous_row). id) AND (current_row.row = previous_row.row -1) `
Едуардо Моралес

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; 

Примітка: Якщо значення не знайдено, воно повернеться в нулеве значення .

У наведеному вище прикладі попереднє значення буде Raja, а значення Next буде нульовим, оскільки наступного значення немає.


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.
Людовик Гійом

Цей запит може бути корисним, щоб дізнатись, чи було видалено записи з таблиці (порушується послідовність стовпця ідентифікатора auto_increment) чи ні.
Дхарманг

@LudovicGuillaume Ви можете бути більш конкретними. Я не розумію вашого зауваження, вибачте. Але запит не містить елементів у попередній_row. *
Уолтер Шрабмайр

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

У мене була така ж проблема, як і в Дена, тому я використав його відповідь і вдосконалив її.

Спочатку виберіть індекс рядків, нічого іншого тут немає.

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 - ідентифікатор поточного запису.

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?

Мій приклад - отримати лише ідентифікатор

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 . Я знаю, що це стара публікація, але початківці розробники повинні знати про це.
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

змінити 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

Мабуть, стовпці вибору оцінюються в порядку, тому спочатку вибираються збережені змінні, а потім оновлюються змінні до нового рядка (вибираючи їх у процесі).

NB: Я не впевнений, що це визначена поведінка, але я використовував це і працює.


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

1

Якщо у вас стовпчик індексу, скажіть id, ви можете повернути попередній та наступний ідентифікатор в одному запиті sql. Замініть: 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

Моє рішення отримати наступний і попередній запис також повернутися до першого запису, якщо я останній і навпаки

Я не використовую ідентифікатор, я використовую заголовок для приємних URL-адрес

Я використовую 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 . Я знаю, що це стара публікація, але початківці розробники повинні знати про це.
ayrtonvwf

0

Ось моя відповідь. Я підбираю ідею з " Гідного даблера " і додаю ту частину, яка перевіряє, чи є id між min (id) та max (id). Ось частина для створення моєї таблиці.

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

);

Наступним кроком є ​​створення збереженої процедури, яка відповідає за отримання попереднього ідентифікатора.

    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
Полювання на знак некроманта? На це запитання було прийнято відповідь протягом 10 років.
Якуб Арнольд

Я щойно знайшов цю тему, тому що недавно отримав ту саму проблему.
Денніс


-1

Я думаю, щоб мати справжній наступний або попередній рядок у таблиці SQL, нам потрібне реальне значення з рівним, (<або>) поверненням більше ніж одне, якщо вам потрібно змінити положення рядка в таблиці упорядкування.

нам потрібне значення $positionдля пошуку neighboursрядка У моїй таблиці я створив стовпець "позиція"

і 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

Якщо рядок було видалено, це порушиться. наприклад: якщо ідентифікатори мають номер 1,2,6,7,8.
Кевін Уотерсон

-2

Виберіть верхній 1 * з foo, де id> 4 порядок за id asc


1
Еквівалентним MySQL є "ЛІМІТ 1": select * from foo where id>4 order by id asc LIMIT 1
моб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.