Wie bekomme ich den nächsten / vorherigen Datensatz in MySQL?

129

Angenommen, ich habe Datensätze mit den IDs 3,4,7,9 und möchte durch Navigation über die nächsten / vorherigen Links von einem zum anderen wechseln können. Das Problem ist, dass ich nicht weiß, wie ich einen Datensatz mit der nächsthöheren ID abrufen soll.

Wenn ich also einen Datensatz mit der ID 4 habe, muss ich in der Lage sein, den nächsten vorhandenen Datensatz abzurufen, nämlich 7. Die Abfrage würde wahrscheinlich ungefähr so ​​aussehen

SELECT * FROM foo WHERE id = 4 OFFSET 1

Wie kann ich den nächsten / vorherigen Datensatz abrufen, ohne die gesamte Ergebnismenge abzurufen und manuell zu iterieren?

Ich benutze MySQL 5.

Jakub Arnold
quelle
8
Sie sollten sich beim Sortieren nicht auf IDs verlassen (dh: Angenommen, Ihre ID ist eine Spalte auto_increment). Sie sollten eine weitere Spalte in der Tabelle erstellen, die die Sortierreihenfolge explizit darstellt.
Anständiger Dabbler

Antworten:

257

Nächster:

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

Bisherige:

select * from foo where id = (select max(id) from foo where id < 4)
langer Hals
quelle
6
Ich denke, die Unterabfragen sollten sein (wählen Sie min (id) von foo, wobei id> 4) und (wählen Sie max (id) von foo, wobei id <4).
Anständiger Dabbler
1
Du hast recht, ich habe die FROM-Klausel vergessen. Vielen Dank für den Hinweis. :)
Longneck
2
Dies ist keine Lösung, denn was passiert, wenn Sie die Zeile aus "foo" entfernen? : P
Valentin Hristov
@valentinhristov Ich verstehe nicht, was du sagst. Bitte geben Sie ein Beispiel.
Longneck
@ Longneck Wenn du nach der Aufnahme postest, ist alles in Ordnung. Wenn Sie jedoch Inhalte nach der Versions-ID veröffentlichen, ist dies kein Kriterium für die Position in der Reihenfolge.
Valentin Hristov
140

Zusätzlich zur Lösung von Cemkalyoncu :

nächste Aufzeichnung:

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

bisherigen Rekord:

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

Bearbeiten: Da diese Antwort in letzter Zeit einige positive Stimmen erhalten hat, möchte ich den Kommentar hervorheben, den ich zuvor zum Verständnis gemacht habe, dass eine Primärschlüsselspalte nicht als zu sortierende Spalte gedacht ist, da MySQL nicht garantiert, dass eine höhere automatische Inkrementierung erfolgt Werte werden notwendigerweise zu einem späteren Zeitpunkt hinzugefügt.

Wenn Sie sich nicht darum kümmern und einfach den Datensatz mit einem höheren (oder niedrigeren) Wert benötigen, reicht iddies aus. Verwenden Sie dies nur nicht, um festzustellen, ob ein Datensatz später (oder früher) tatsächlich hinzugefügt wird. Verwenden Sie stattdessen beispielsweise eine datetime-Spalte zum Sortieren.

Anständiger Dabbler
quelle
Ich bevorzuge diese Antwort sehr, ich sortiere nach Zeitstempel und es bedeutet keine Unterabfragen, also insgesamt 2 Abfragen statt 4.
Maurice
61

Alle oben genannten Lösungen erfordern zwei Datenbankaufrufe. Der folgende SQL-Code kombiniert zwei SQL-Anweisungen zu einer.

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)
      )    
Sudip
quelle
3
Auf jeden Fall die beste Lösung. Schnell und alles in einer Abfrage.
Daemon
Funktioniert perfekt, auch bei einer viel komplizierteren Abfrage!
Taiar
Das Hinzufügen DISTINCTvor *, macht es noch besser, das heißt natürlich, wenn ides 0als Wert haben kann.
Ejaz
Hervorragende Lösung. Beim ersten und letzten Datensatz wird ideher Single als zwei zurückgegeben.
Lubosdz
19
SELECT * FROM foo WHERE id>4 ORDER BY id LIMIT 1
Cem Kalyoncu
quelle
1
+1 Ich denke, das ist die sauberste Lösung. Aber die LIMIT-Klausel sollte zuletzt kommen: SELECT * FROM foo WHERE id> 4 ORDER BY id LIMIT 1
Decent Dabbler
und SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1für die vorherige Aufzeichnung
fwonce
12

Ich habe versucht, etwas Ähnliches zu tun, aber ich brauchte die nach Datum geordneten Ergebnisse, da ich mich nicht auf das ID-Feld als sortierbare Spalte verlassen kann. Hier ist die Lösung, die ich gefunden habe.

Zuerst ermitteln wir den Index des gewünschten Datensatzes in der Tabelle, wenn er nach unseren Wünschen sortiert ist:

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;

Dann dekrementiere das Ergebnis um 2 und setze es in die limit-Anweisung. Zum Beispiel hat das oben Gesagte 21 für mich zurückgegeben, also renne ich:

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

Gibt Ihnen Ihren primären Datensatz zusammen mit den nächsten und vorherigen Datensätzen in Ihrer angegebenen Reihenfolge.

Ich habe versucht, dies als einzelnen Datenbankaufruf auszuführen, konnte jedoch die LIMIT-Anweisung nicht dazu bringen, eine Variable als einen ihrer Parameter zu verwenden.

Dan
quelle
Ihr Ansatz interessiert mich. Was ist, wenn Sie auch einen JOIN machen müssen? Sagen wir, AUS Artikeln a JOIN Autoren b ON b.this = a.that. Es scheint mir, dass der JOIN die Bestellung durcheinander bringt. Hier ist ein Beispiel pastebin.com/s7R62GYV
idrarig
1
Um einen JOIN durchzuführen, müssen Sie zwei verschiedene @ -Variablen definieren. `SELECT current.row, current.id, previous.row, previous.id FROM (SELECT @rownum: = @ rownum + 1 Zeile, a. * FROM Artikel a, (SELECT @rownum: = 0) r ORDER BY date, id) als current_row LEFT JOIN (SELECT @ rownum2: = @ rownum2 + 1 Zeile, a. * FROM Artikel a, (SELECT @ rownum2: = 0) r ORDER BY date, id) als previous_row ON (current_row.id = previous_row. id) AND (current_row.row = previous_row.row -1) `
Eduardo Moralles
7

Versuchen Sie dieses Beispiel.

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; 

Hinweis: Wenn der Wert nicht gefunden wird, wird null zurückgegeben .

Im obigen Beispiel ist der vorherige Wert Raja und der nächste Wert null, da es keinen nächsten Wert gibt.

Ranga Reddy
quelle
7

Mit dem Ansatz von @Dan können Sie JOINs erstellen. Verwenden Sie einfach eine andere @ -Variable für jede Unterabfrage.

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)
Eduardo Moralles
quelle
Genau das, was ich brauchte, danke. Beachten Sie, dass Ihre Auswahl nicht korrekt ist, current-> current_row& previous-> previous_row.
Ludovic Guillaume
Diese Abfrage kann hilfreich sein, um festzustellen, ob Datensätze aus der Tabelle gelöscht wurden (die Sequenz der Spalte "auto_increment id" ist unterbrochen) oder nicht.
Dharmang
@ LudovicGuillaume können Sie genauer sein. Ich verstehe Ihre Bemerkung nicht, sorry. Aber die Abfrage hat keine Elemente in previous_row. *
Walter Schrabmair
4

Nächste Reihe

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

Vorherige Zeile

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

Probe nächste Reihe

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

Beispiel vorherige Zeile

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
IDALICIUS
quelle
4

Ich hatte das gleiche Problem wie Dan, also habe ich seine Antwort verwendet und verbessert.

Wählen Sie zuerst den Zeilenindex aus, hier nichts anderes.

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;

Verwenden Sie nun zwei separate Abfragen. Wenn der Zeilenindex beispielsweise 21 ist, lautet die Abfrage zur Auswahl des nächsten Datensatzes:

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

Verwenden Sie diese Abfrage, um den vorherigen Datensatz auszuwählen:

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

Beachten Sie, dass für die erste Zeile (Zeilenindex ist 1) das Limit auf -1 gesetzt wird und Sie einen MySQL-Fehler erhalten. Sie können eine if-Anweisung verwenden, um dies zu verhindern. Wählen Sie einfach nichts aus, da es sowieso keinen vorherigen Datensatz gibt. Im Fall des letzten Datensatzes wird es die nächste Zeile geben und daher wird es kein Ergebnis geben.

Beachten Sie auch, dass wenn Sie DESC anstelle von ASC zum Bestellen verwenden, die Abfragen zur Auswahl der nächsten und vorherigen Zeilen immer noch gleich sind, jedoch umgeschaltet werden.

magnetronnie
quelle
3

Dies ist eine universelle Lösung für Bedingungen mit mehr gleichen Ergebnissen.

<?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
?>
asdf2017
quelle
3

Hier haben wir eine Möglichkeit, vorherige und nächste Datensätze mit einer einzelnen MySQL-Abfrage abzurufen. Wobei 5 die ID des aktuellen Datensatzes ist.

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) )
Jaskaran Singh
quelle
2

Wie bekomme ich den nächsten / vorherigen Datensatz in MySQL & PHP?

Mein Beispiel ist, nur die ID zu erhalten

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;
  }
}
Purvesh Tejani
quelle
Downvoted, da es für SQL Injection anfällig ist . Ich weiß, dass dies ein alter Beitrag ist, aber Anfänger sollten sich dessen bewusst sein.
Ayrtonvwf
2

Optimieren des @ Don-Ansatzes, um nur eine Abfrage zu verwenden

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

Ändern Sie CurrentArticleID mit der aktuellen Artikel-ID wie

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
Zeeshan Anjum
quelle
Dies ist die nützlichste Antwort, die ich bisher hier gefunden habe. Die einzige wirkliche Verbesserung, die ich vorschlagen würde, ist die Verwendung von Variablen und das Verschieben aller eindeutigen Teile nach oben, um leicht bearbeitet werden zu können. Dann kann leicht in eine häufig referenzierte Funktion oder einen Prozess verwiesen werden.
Liljoshu
1

Es gibt einen weiteren Trick, mit dem Sie Spalten aus vorherigen Zeilen in beliebiger Reihenfolge mit einer Variablen anzeigen können, die dem @ row-Trick ähnelt:

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

Anscheinend werden die ausgewählten Spalten der Reihe nach ausgewertet, sodass zuerst die gespeicherten Variablen ausgewählt und dann die Variablen in der neuen Zeile aktualisiert werden (wobei sie dabei ausgewählt werden).

NB: Ich bin nicht sicher, ob dies ein definiertes Verhalten ist, aber ich habe es verwendet und es funktioniert.

Willem M.
quelle
1

Wenn Sie Ihrer Anfrage mehr als eine gebenid möchten, erhalten Sie next_idfür alle ...

Weisen Sie cur_idin Ihrem Auswahlfeld zu und geben Sie es dann an die Unterabfrage weiter, die next_idin das Auswahlfeld gelangt . Und dann wählen Sie einfach next_id.

Verwenden der Longneck-Antwort zum Berechnen 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;
Romeno
quelle
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
Piotr Kos
quelle
6
Willkommen bei StackOverflow. Antworten, die ausschließlich aus Code bestehen, sind selten nützlich. Bitte erläutern Sie, was Ihr Code tut. Eine In-Code-Dokumentation und / oder Variablennamen in Englisch wären ebenfalls hilfreich.
Politank-Z
1

Wenn Sie eine Indexspalte haben, z. B. ID, können Sie die vorherige und die nächste ID in einer SQL-Anforderung zurückgeben. Ersetzen Sie: id durch Ihren Wert

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
Patrice Flao
quelle
0

Meine Lösung, um den nächsten und Vorschau-Datensatz zu erhalten, auch, um zum ersten Datensatz zurückzukehren, wenn ich bis zum letzten bin und umgekehrt

Ich benutze nicht die ID, ich benutze den Titel für nette URLs

Ich benutze 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;
    }     
}
toiglicher
quelle
Downvoted, da es für SQL Injection anfällig ist . Ich weiß, dass dies ein alter Beitrag ist, aber Anfänger sollten sich dessen bewusst sein.
Ayrtonvwf
0

Hier ist meine Antwort. Ich greife eine Idee von ' Decent Dabbler ' auf und füge den Teil hinzu, der prüft, ob die ID zwischen min (id) und max (id) liegt. Hier ist der Teil zum Erstellen meiner Tabelle.

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

);

Der nächste Schritt ist das Erstellen einer gespeicherten Prozedur, die für das Abrufen der vorherigen ID verantwortlich ist.

    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

Die erste Methode ist gut, wenn die Indizes sortiert sind, aber nicht. Zum Beispiel, wenn Sie Indizes haben: 1,2,7 und Sie müssen Index Nummer 2 auf diese Weise viel besser erhalten, um einen anderen Ansatz zu verwenden.

    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
Dennis
quelle
1
Auf der Suche nach dem Nekromantenabzeichen? Diese Frage wird seit 10 Jahren akzeptiert.
Jakub Arnold
Ich habe dieses Thema gerade gefunden, weil ich kürzlich das gleiche Problem hatte.
Dennis
0

100% arbeiten

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

Ich denke, um die reale nächste oder vorherige Zeile in der SQL-Tabelle zu haben, benötigen wir den realen Wert mit gleich (<oder>), der mehr als eins zurückgibt, wenn Sie die Position der Zeile in einer Reihenfolgetabelle ändern müssen.

Wir brauchen den Wert $position, um die neighboursZeile zu durchsuchen. In meiner Tabelle habe ich eine Spalte 'Position' erstellt.

Die SQL-Abfrage zum Abrufen der erforderlichen Zeile lautet:

Fürs nächste :

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

für vorherige:

 SELECT * 
 FROM my_table 
 WHERE id = (SELECT (id) 
             FROM my_table 
             WHERE `position` = ($position-1)) 
 LIMIT 1
Roussos
quelle
Wenn eine Zeile gelöscht wurde, wird dies unterbrochen. Beispiel: Wenn die IDs die Nummer 1,2,6,7,8 haben.
Kevin Waterson
-2

Wählen Sie Top 1 * aus foo, wobei ID> 4 nach ID aufsteigen

Gratzy
quelle
1
Äquivalentes MySQL ist "LIMIT 1": select * from foo where id>4 order by id asc LIMIT 1
Mob