web-dev-qa-db-fra.com

Comment obtenir l'enregistrement suivant/précédent dans MySQL?

Supposons que j'ai des enregistrements avec les identifiants 3,4,7,9 et que je veux pouvoir aller de l'un à l'autre en naviguant via les liens suivant/précédent. Le problème est que je ne sais pas comment récupérer un enregistrement avec l’ID le plus proche.

Ainsi, lorsque j'ai un enregistrement avec l'ID 4, je dois pouvoir récupérer le prochain enregistrement existant, qui sera 7. La requête ressemblera probablement à quelque chose comme:

SELECT * FROM foo WHERE id = 4 OFFSET 1

Comment puis-je récupérer l'enregistrement suivant/précédent sans récupérer l'ensemble des résultats et l'itérer manuellement?

J'utilise MySQL 5.

115
Jakub Arnold

suivant:

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

précédent:

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

En plus de la solution (cemkalyoncu } _:

prochain enregistrement:

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

enregistrement précédent:

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

edit: Étant donné que cette réponse a reçu quelques votes positifs dernièrement, je tiens vraiment à souligner le commentaire que j'ai fait précédemment sur la compréhension du fait qu'une colonne de clé primaire n'est pas conçue comme une colonne de tri, parce que MySQL ne garantit pas que les valeurs supérieures, automatiquement incrémentées, sont nécessairement ajoutées ultérieurement.

Si vous ne vous souciez pas de cela et que vous avez simplement besoin de l'enregistrement avec une valeur plus élevée (ou plus basse) id, cela suffira. N'utilisez simplement pas cela pour déterminer si un enregistrement est réellement ajouté plus tard (ou plus tôt). Au lieu de cela, envisagez d'utiliser une colonne datetime pour trier, par exemple.

132
Decent Dabbler

Toutes les solutions ci-dessus nécessitent deux appels de base de données. Le code SQL ci-dessous combine deux instructions SQL en une.

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)
      )    
53
sudip
SELECT * FROM foo WHERE id>4 ORDER BY id LIMIT 1
19
Cem Kalyoncu

J'essayais de faire quelque chose de similaire à cela, mais j'avais besoin des résultats classés par date, car je ne peux pas compter sur le champ ID comme colonne triable. Voici la solution que j'ai trouvée.

Nous trouvons d’abord l’index de l’enregistrement souhaité dans la table, quand il est trié comme nous le voulons:

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;

Puis décrémentez le résultat de 2 et placez-le dans l'instruction limit. Par exemple, ce qui précède m'a renvoyé 21, aussi je lance:

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

Vous donne votre enregistrement principal ainsi que ses enregistrements suivants et précédents en fonction de votre ordre indiqué.

J'ai essayé de le faire en tant qu'appel de base de données unique, mais je ne pouvais pas obtenir que l'instruction LIMIT prenne une variable comme l'un de ses paramètres.

11
Dan

Essayez cet exemple. 

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; 

Remarque: Si value n'est pas trouvé, il renverra null .

Dans l'exemple ci-dessus, Previous value sera Raja et Next value sera null car il n'y a pas de prochaine valeur.

7
Ranga Reddy

En utilisant l'approche de @Dan, vous pouvez créer des JOIN. Utilisez simplement une variable différente pour chaque requête.

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)
6
Eduardo Moralles

Rangée suivante

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

Rangée précédente

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

échantillon suivant

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

échantillon de la rangée précédente

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
5
IDALICIUS

J'ai eu le même problème que Dan, alors j'ai utilisé sa réponse pour l'améliorer.

Commencez par sélectionner l'index des lignes, rien de différent ici.

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;

Maintenant, utilisez deux requêtes distinctes. Par exemple, si l'index de ligne est 21, la requête pour sélectionner le prochain enregistrement sera:

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

Pour sélectionner l'enregistrement précédent, utilisez cette requête:

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

Gardez à l'esprit que pour la première ligne (l'index de ligne est 1), la limite passera à -1 et vous obtiendrez une erreur MySQL. Vous pouvez utiliser une instruction if pour empêcher cela. Il suffit de ne rien sélectionner, car il n’existe aucun enregistrement précédent. Dans le cas du dernier enregistrement, il y aura la rangée suivante et par conséquent, il n'y aura aucun résultat.

N'oubliez pas non plus que si vous utilisez DESC pour la commande, au lieu d'ASC, les requêtes visant à sélectionner les lignes suivante et précédente restent identiques, mais commutées.

4
magnetronnie

C'est une solution universelle pour des conditions avec plus de résultats identiques.

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

Comment obtenir l'enregistrement suivant/précédent dans MySQL et PHP?

Mon exemple est d'obtenir uniquement l'identifiant

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;
  }
}
2
Purvesh Tejani

Nous avons ici un moyen de récupérer les enregistrements précédents et suivants en utilisant une seule requête MySQL . Où 5 est l'id de l'enregistrement en cours.

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
Jaskaran Singh

Optimiser l'approche @Don pour n'utiliser qu'une seule requête 

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

changer CurrentArticleID avec l'ID d'article actuel comme 

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
2
Zeeshan Anjum

Si vous voulez introduisez plus d'une iddans votre requête et obtenez next_id pour chacun d'entre eux ...

Attribuez cur_id dans votre champ de sélection, puis transmettez-le à la sous-requête en obtenant next_id dans le champ de sélection. Et puis sélectionnez juste next_id.

Utilisation de longneck pour répondre à 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
Romeno

Si vous avez une colonne d'index, dites id, vous pouvez renvoyer l'id précédent et l'id suivant dans une requête SQL . Remplacez: id par votre valeur

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
1
Patrice Flao

Une autre astuce que vous pouvez utiliser pour afficher les colonnes des lignes précédentes, en utilisant l'ordre de votre choix, en utilisant une variable similaire à l'astuce @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

Apparemment, les colonnes de sélection sont évaluées dans l’ordre, ce qui sélectionne d’abord les variables sauvegardées, puis met à jour les variables dans la nouvelle ligne (en les sélectionnant dans le processus).

NB: Je ne suis pas sûr qu'il s'agisse d'un comportement défini, mais je l'ai utilisé et cela fonctionne.

1
Willem M.
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
1
Piotr Kos

Ma solution pour obtenir l’enregistrement suivant et les aperçus Ainsi que pour revenir au premier enregistrement si j’en suis au dernier et inversement

Je n'utilise pas l'identifiant J'utilise le titre pour Nice url's

J'utilise 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;
    }     
}
0
toiglicher

Voici ma réponse. Je prends une idée de ' Decent Dabbler ' et ajoute la partie qui vérifie si id est entre min (id) et max (id). Voici la partie pour créer ma table.

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

)

La prochaine étape consiste à créer une procédure stockée chargée d’obtenir l’id précédent.

    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

La première méthode est bonne si les index sont triés, mais s'ils ne le sont pas. Par exemple, si vous avez des index: 1, 2, 7 et que vous devez obtenir l'index numéro 2 de cette manière, il est préférable d'utiliser une autre approche. 

    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
0
Dennis