¿Cómo obtener el registro siguiente / anterior en MySQL?

129

Digamos que tengo registros con ID 3,4,7,9 y quiero poder ir de uno a otro navegando a través de los enlaces siguiente / anterior. El problema es que no sé cómo obtener el registro con la ID más alta más cercana.

Entonces, cuando tengo un registro con ID 4, necesito poder recuperar el siguiente registro existente, que sería 7. La consulta probablemente se vería algo así como

SELECT * FROM foo WHERE id = 4 OFFSET 1

¿Cómo puedo obtener el registro siguiente / anterior sin obtener todo el conjunto de resultados e iterar manualmente?

Estoy usando MySQL 5.

Jakub Arnold
fuente
8
No debe confiar en las ID para ordenar (es decir: suponiendo que su ID es una columna de aumento automático). Debe crear otra columna en la tabla que represente explícitamente el orden de clasificación.
Dabbler decente

Respuestas:

258

siguiente:

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

anterior:

select * from foo where id = (select max(id) from foo where id < 4)
cuello largo
fuente
77
Creo que las subconsultas deberían ser (seleccione min (id) de foo donde id> 4) y (seleccione max (id) de foo donde id <4).
Dabbler decente
1
tienes razón, olvidé la cláusula FROM. Gracias por mencionarlo. :)
longneck
2
Esto no es una solución porque si quita la fila de "foo", ¿qué sucederá? : P
Valentin Hristov
@valentinhristov No entiendo lo que estás diciendo. Por favor proporcione un ejemplo.
cuello largo
@longneck Si publica después de grabar todo está bien. Pero si publica contenido después de la identificación de la versión no es criterio para la posición en secuencia.
Valentin Hristov
140

Además de cemkalyoncu's solución :

siguiente registro:

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

récord anterior:

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

editar: dado que esta respuesta ha recibido algunos votos positivos últimamente, realmente quiero enfatizar el comentario que hice antes sobre entender que una columna de clave principal no está pensada como una columna para clasificar, porque MySQL no garantiza que aumente automáticamente , los valores se agregan necesariamente en un momento posterior.

Si no le importa esto, y simplemente necesita el registro con un valor más alto (o más bajo) id, esto será suficiente. Simplemente no use esto como un medio para determinar si un registro se agrega realmente más tarde (o antes). En su lugar, considere usar una columna de fecha y hora para ordenar, por ejemplo.

Dabbler decente
fuente
Prefiero esta respuesta, ordeno por marca de tiempo, y significa que no hay subconsultas, por lo que 2 consultas en total en lugar de 4.
Maurice
61

Todas las soluciones anteriores requieren dos llamadas a la base de datos. El siguiente código sql combina dos declaraciones sql en una.

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)
      )    
revs sudip
fuente
3
Definitivamente la mejor solución. Rápido y todo en una consulta.
demonio
¡Funciona perfectamente incluso en una consulta mucho más complicada!
taiar
Agregar DISTINCTantes *lo hará aún mejor, es decir, por supuesto, si idpuede tener 0como valor.
Ejaz
Excelente solucion. El primer y último registro devolverá solo en idlugar de dos.
lubosdz
19
SELECT * FROM foo WHERE id>4 ORDER BY id LIMIT 1
Cem Kalyoncu
fuente
1
+1 Creo que esta es la solución más limpia. Pero la cláusula LIMIT debería ser la última: SELECCIONE * de foo WHERE id> 4 ORDER BY id LIMIT 1
Dabbler decente
y SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1para el registro previouse
fwonce
12

Intentaba hacer algo similar a esto, pero necesitaba los resultados ordenados por fecha, ya que no puedo confiar en el campo ID como una columna ordenable. Aquí está la solución que se me ocurrió.

Primero descubrimos el índice del registro deseado en la tabla, cuando está ordenado como queremos:

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;

Luego decremente el resultado en 2 y colóquelo en la declaración de límite. Por ejemplo, lo anterior me devolvió 21, así que ejecuto:

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

Le da su registro primario junto con sus registros siguientes y anteriores dado su pedido declarado.

Traté de hacerlo como una sola llamada a la base de datos, pero no pude obtener la declaración LIMIT para tomar una variable como uno de sus parámetros.

Dan
fuente
Estoy interesado en tu enfoque. ¿Qué pasa si también necesitas unirte? Digamos, DE artículos a UNIR autores b EN b.Este = a.que. Me parece que JOIN estropea el pedido. Aquí hay un ejemplo pastebin.com/s7R62GYV
idrarig
1
Para hacer un JOIN, debe definir dos variables @ diferentes. `SELECCIONAR current.row, current.id, previous.row, previous.id FROM (SELECT @rownum: = @ rownum + 1 fila, a. * FROM artículos a, (SELECT @rownum: = 0) r ORDER BY fecha, id) como current_row LEFT JOIN (SELECT @ rownum2: = @ rownum2 + 1 fila, a. * FROM artículos a, (SELECT @ rownum2: = 0) r ORDER BY date, id) como previous_row ON (current_row.id = previous_row. id) AND (current_row.row = previous_row.row -1) `
Eduardo Moralles
7

Prueba este ejemplo.

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; 

Nota: Si no se encuentra el valor , devolverá nulo .

En el ejemplo anterior, el valor anterior será Raja y el valor siguiente será nulo porque no hay un valor siguiente.

Ranga Reddy
fuente
7

Usando el enfoque de @Dan, puede crear uniones. Simplemente use una variable @ diferente para cada subconsulta.

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
fuente
Justo lo que necesitaba, gracias. Tenga en cuenta que su selección no es correcta, current-> current_row& previous-> previous_row.
Ludovic Guillaume
Esta consulta puede ser útil para encontrar si los registros se eliminaron de la tabla (la secuencia de la columna de identificación de aumento automático se está rompiendo) o no.
Dharmang
@LudovicGuillaume puedes ser más específico. No entiendo tu comentario, lo siento. Pero la consulta no tiene elementos en previous_row. *
Walter Schrabmair
4

Fila siguiente

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

Fila anterior

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

muestra siguiente fila

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

muestra fila anterior

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
fuente
4

Tenía el mismo problema que Dan, así que usé su respuesta y la mejoré.

Primero seleccione el índice de fila, nada diferente aquí.

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;

Ahora use dos consultas separadas. Por ejemplo, si el índice de la fila es 21, la consulta para seleccionar el siguiente registro será:

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

Para seleccionar el registro anterior, use esta consulta:

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

Tenga en cuenta que para la primera fila (el índice de fila es 1), el límite irá a -1 y obtendrá un error de MySQL. Puede usar una declaración if para evitar esto. Simplemente no seleccione nada, ya que no hay registro anterior de todos modos. En el caso del último registro, habrá la siguiente fila y por lo tanto no habrá resultado.

También tenga en cuenta que si usa DESC para ordenar, en lugar de ASC, sus consultas para seleccionar las filas siguiente y anterior siguen siendo las mismas, pero cambiadas.

magnetronnie
fuente
3

Esta es una solución universal para condiciones con más resultados iguales.

<?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
fuente
3

Aquí tenemos una forma de obtener registros anteriores y siguientes utilizando una sola consulta MySQL. Donde 5 es la identificación del registro actual.

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
fuente
2

¿Cómo obtener el registro siguiente / anterior en MySQL y PHP?

Mi ejemplo es obtener solo la identificación

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
fuente
Vota abajo porque es vulnerable a la inyección SQL . Sé que esta es una publicación antigua, pero los desarrolladores novatos deberían estar al tanto.
ayrtonvwf
2

Optimizando el enfoque @Don para usar solo una consulta

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

cambiar CurrentArticleID con ID de artículo actual como

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
fuente
Esta es la respuesta más útil que he encontrado aquí hasta ahora. La única mejora real que sugeriría es usar variables y mover todas las partes únicas hacia arriba para que sean fácilmente editables. Luego se puede convertir fácilmente en una función o proceso a menudo referenciado.
liljoshu
1

Hay otro truco que puede usar para mostrar columnas de filas anteriores, usando cualquier orden que desee, usando una variable similar al truco @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

Aparentemente, las columnas seleccionadas se evalúan en orden, por lo que esto primero seleccionará las variables guardadas y luego actualizará las variables a la nueva fila (seleccionándolas en el proceso).

NB: No estoy seguro de que este sea un comportamiento definido, pero lo he usado y funciona.

Willem M.
fuente
1

Si desea alimentar más de unoid a su consulta y obtener next_idtodos ellos ...

Asigne cur_idsu campo de selección y luego aliméntelo a la subconsulta para ingresar al next_idcampo de selección. Y luego seleccione solo next_id.

Usando la respuesta de cuello largo para 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;
Romeno
fuente
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
fuente
66
Bienvenido a StackOverflow. Las respuestas que consisten completamente en código rara vez son útiles. Considere proporcionar alguna explicación de lo que está haciendo su código. La documentación en código y / o los nombres de variables en inglés también serían útiles.
Politank-Z
1

Si tiene una columna de índice, diga id, puede devolver la identificación anterior y la siguiente en una solicitud sql. Reemplazar: id con su valor

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

Mi solución para obtener el siguiente registro y las vistas previas también para volver al primer registro si estoy en el último y viceversa

No estoy usando la identificación, estoy usando el título para buenas URL

Estoy usando 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
fuente
Vota abajo porque es vulnerable a la inyección SQL . Sé que esta es una publicación antigua, pero los desarrolladores novatos deberían estar al tanto.
ayrtonvwf
0

Aquí está mi respuesta. Tomo una idea de ' Decent Dabbler ' y agrego la parte que verifica si la identificación está entre min (id) y max (id). Aquí está la parte para crear mi tabla.

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

);

El siguiente paso es crear un procedimiento almacenado que sea responsable de obtener la identificación anterior.

    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

El primer método es bueno si los índices están ordenados, pero si no lo están. Por ejemplo, si tiene índices: 1,2,7 y necesita obtener el índice número 2 de esta manera mucho mejor para usar otro enfoque.

    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
fuente
1
¿Estás buscando la insignia de nigromante? Esta pregunta ha tenido una respuesta aceptada durante 10 años.
Jakub Arnold
Acabo de encontrar este tema, porque recientemente tuve el mismo problema.
Dennis
0

100% trabajando

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

Creo que para tener la fila real siguiente o anterior en la tabla SQL, necesitamos que el valor real con igual, (<o>) devuelva más de uno si necesita cambiar la posición de la fila en una tabla de pedidos.

necesitamos el valor $positionpara buscar la neighboursfila En mi tabla creé una columna 'posición'

y la consulta SQL para obtener la fila necesaria es:

para el siguiente:

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

para el anterior:

 SELECT * 
 FROM my_table 
 WHERE id = (SELECT (id) 
             FROM my_table 
             WHERE `position` = ($position-1)) 
 LIMIT 1
Roussos
fuente
Si se ha eliminado una fila, esto se romperá. por ejemplo: si las identificaciones son el número 1,2,6,7,8.
Kevin Waterson
-2

Seleccione top 1 * de foo donde id> 4 ordenar por id asc

Gratzy
fuente
1
MySQL equivalente es "LIMIT 1": select * from foo where id>4 order by id asc LIMIT 1
mob