Como obter o registro seguinte / anterior no MySQL?


129

Digamos que tenho registros com IDs 3,4,7,9 e desejo poder ir de um para outro navegando nos links seguintes / anteriores. O problema é que eu não sei como buscar registros com o ID mais alto mais próximo.

Portanto, quando tenho um registro com o ID 4, preciso buscar o próximo registro existente, que seria 7. A consulta provavelmente se parecerá com algo como

SELECT * FROM foo WHERE id = 4 OFFSET 1

Como posso buscar o registro seguinte / anterior sem buscar todo o conjunto de resultados e iterar manualmente?

Estou usando o MySQL 5.


8
Você não deve confiar nos IDs para classificar (ou seja: presumindo que seu ID seja uma coluna de incremento automático). Você deve criar outra coluna na tabela que descreva explicitamente a ordem de classificação.
Digno Dabbler

Respostas:


258

Próximo:

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)

7
Eu acho que as subconsultas devem ser (selecione min (id) de foo onde id> 4) e (selecione max (id) de foo onde id <4).
Digno Dabbler

1
você está certo, eu esqueci a cláusula FROM. Obrigado por apontar isso. :) #
30909 longneck

2
Isso não é solução, porque se você remover a linha de "foo", o que acontecerá? : P
Valentin Hristov

@valentinhristov Não entendo o que você está dizendo. Por favor, forneça um exemplo.
longneck

@ longneck Se você postar após a gravação, está tudo bem. Mas se você postar conteúdo após o ID da versão, não será um critério para posição em sequência.
Valentin Hristov

140

Além dos cemkalyoncu's solução :

próximo registro:

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

Registro anterior:

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

edit: Como essa resposta tem recebido alguns votos positivos recentemente, quero enfatizar o comentário que fiz anteriormente sobre o entendimento de que uma coluna de chave primária não se destina a ser uma coluna a ser classificada, porque o MySQL não garante esse aumento automático maior , os valores são necessariamente adicionados posteriormente.

Se você não se importa com isso, e simplesmente precisa do registro com um valor mais alto (ou mais baixo) id, isso será suficiente. Apenas não use isso como um meio para determinar se um registro é realmente adicionado mais tarde (ou mais cedo). Em vez disso, considere usar uma coluna datetime para classificar por, por exemplo.


Eu prefiro muito essa resposta, classifico por carimbo de data e hora, e isso significa que não há subconsultas, portanto, duas consultas no total, em vez de 4.
Maurice

61

Todas as soluções acima requerem duas chamadas ao banco de dados. O código sql abaixo combina duas instruções sql em uma.

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
Definitivamente a melhor solução. Rápido e tudo em uma consulta.
Daemon

Funciona perfeitamente, mesmo em uma consulta muito mais complicada!
taiar 25/07/14

Adicionar DISTINCTantes *tornará ainda melhor, ou seja, é claro que idpode ter 0como valor.
Ejaz

Excelente solução. No primeiro e no último registro retornará um single idao invés de dois.
lubosdz

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

1
+1 Acho que esta é a solução mais limpa. Mas a cláusula LIMIT deve vir por último: SELECT * FROM foo WHERE id> 4 ORDER BY id LIMIT 1
Dabbler decente

e SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1para o registro anterior
fwonce 12/03/2015

12

Eu estava tentando fazer algo semelhante a isso, mas precisava dos resultados ordenados por data, pois não posso confiar no campo de ID como uma coluna classificável. Aqui está a solução que eu criei.

Primeiro, descobrimos o índice do registro desejado na tabela, quando ele é classificado 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;

Em seguida, diminua o resultado em 2 e coloque-o na declaração de limite. Por exemplo, o acima retornou 21 para mim, então eu corro:

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

Fornece seu registro primário, juntamente com os registros seguintes e anteriores, de acordo com o pedido declarado.

Tentei fazer isso como uma única chamada de banco de dados, mas não consegui que a instrução LIMIT recebesse uma variável como um de seus parâmetros.


Estou interessado em sua abordagem. E se você precisar fazer um JOIN também? Digamos, DE artigos a JOIN autores b ON b.este = a.that. Parece-me que o JOIN atrapalha a encomenda. Aqui está um exemplo pastebin.com/s7R62GYV
idrarig 2/13/13

1
Para fazer um JOIN, é necessário definir duas variáveis ​​@ diferentes. `SELECT current.row, current.id, previous.row, previous.id FROM (SELECT @rownum: = @ rownum + 1 linha, a. * FROM artigos a, (SELECT @rownum: = 0) r ORDER BY date, id) como current_row LEFT JOIN (SELECT @ rownum2: = @ rownum2 + 1 linha, a. * FROM artigos 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

Tente este exemplo.

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: Se o valor não for encontrado, ele retornará nulo .

No exemplo acima, o valor Anterior será Raja e o valor Próximo será nulo porque não há próximo valor.


7

Usando a abordagem do @Dan, você pode criar JOINs. Basta usar uma variável @ 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)

Exatamente o que eu precisava, obrigado. Observe que sua seleção não está correta, current-> current_row& previous-> previous_row.
Ludovic Guillaume

Essa consulta pode ser útil para descobrir se os registros foram excluídos da tabela (a sequência da coluna do ID de auto_increment está sendo interrompida) ou não.
Dharmang

@LudovicGuillaume você pode ser mais específico. Eu não entendo sua observação, desculpe. Mas a consulta não possui elementos na anterior_row. *
Walter Schrabmair

4

Próxima linha

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

Linha anterior

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

amostra da próxima linha

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

amostra da linha 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

4

Eu tinha o mesmo problema que Dan, então usei sua resposta e a melhorei.

Primeiro selecione o índice da linha, nada de diferente aqui.

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;

Agora use duas consultas separadas. Por exemplo, se o índice da linha for 21, a consulta para selecionar o próximo registro será:

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

Para selecionar o registro anterior, use esta consulta:

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

Lembre-se de que, para a primeira linha (o índice da linha é 1), o limite será -1 e você receberá um erro do MySQL. Você pode usar uma instrução if para evitar isso. Apenas não selecione nada, pois não há registro anterior. No caso do último registro, haverá a próxima linha e, portanto, não haverá resultado.

Lembre-se também de que, se você usar DESC para fazer pedidos, em vez de ASC, as consultas para selecionar as linhas seguinte e anterior ainda serão as mesmas, mas alternadas.


3

Esta é uma solução universal para condições com mais resultados iguais.

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

Aqui temos uma maneira de buscar registros anteriores e seguintes usando uma única consulta MySQL. Onde 5 é o ID do registro atual.

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

Como obter o registro seguinte / anterior no MySQL e PHP?

Meu exemplo é obter apenas o ID

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;
  }
}

Voto negativo porque é vulnerável à injeção de SQL . Eu sei que este é um post antigo, mas desenvolvedores iniciantes devem estar cientes disso.
Ayrtonvwf # 25/18

2

Otimizando a abordagem @Don para usar apenas uma 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

altere CurrentArticleID com o ID atual do artigo, 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

Esta é a resposta mais útil que encontrei aqui até agora. A única melhoria real que eu sugiro é o uso de variáveis ​​e a movimentação de todas as partes únicas para o topo, para serem facilmente editáveis. Em seguida, pode ser facilmente transformado em uma função ou processo frequentemente referenciado.
liljoshu

1

Há outro truque que você pode usar para mostrar as colunas das linhas anteriores, usando qualquer ordem que desejar, usando uma variável semelhante ao truque @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, as colunas de seleção são avaliadas em ordem, portanto, primeiro você seleciona as variáveis ​​salvas e depois atualiza as variáveis ​​para a nova linha (selecionando-as no processo).

NB: Não tenho certeza se esse é um comportamento definido, mas eu o usei e funciona.


1

Se você deseja alimentar mais de umid na sua consulta e obternext_id todos eles ...

Atribua cur_idseu campo de seleção e alimente-o para que a subconsulta chegue ao next_idcampo de seleção. E então selecione apenasnext_id .

Usando longneck reply 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;

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
Bem-vindo ao StackOverflow. Respostas consistindo inteiramente de código raramente são úteis. Por favor, considere fornecer algumas explicações sobre o que seu código está fazendo. A documentação em código e / ou nomes de variáveis ​​em inglês também seriam úteis.
Politank Z-

1

Se você tiver uma coluna de índice, digamos id, poderá retornar o ID anterior e o próximo em uma solicitação sql. Substitua: id pelo seu 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

0

Minha solução para obter o próximo registro e visualizar também para voltar ao primeiro registro, se eu estiver na última e vice-versa

Não estou usando o ID, estou usando o título para URLs agradáveis

Estou usando o 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;
    }     
}

Voto negativo porque é vulnerável à injeção de SQL . Eu sei que este é um post antigo, mas desenvolvedores iniciantes devem estar cientes disso.
Ayrtonvwf # 25/18

0

Aqui está a minha resposta. Pego uma ideia no ' Decent Dabbler ' e adiciono a parte que está verificando se o id está entre min (id) e max (id). Aqui está a parte para criar minha tabela.

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

);

A próxima etapa é criar um procedimento armazenado responsável por obter o ID 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

O primeiro método é bom se os índices estiverem classificados, mas se não estiverem. Por exemplo, se você possui índices: 1,2,7 e precisa obter o número 2 do índice dessa maneira, muito melhor para usar outra abordagem.

    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
Procurando o distintivo necromante? Esta pergunta tem uma resposta aceita há 10 anos.
Jakub Arnold

Acabei de encontrar este tópico, porque tive o mesmo problema recentemente.
Dennis

0

100% trabalhando

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

-1

Eu acho que para ter a linha real seguinte ou anterior na tabela SQL, precisamos do valor real com igual, (<ou>) retorne mais de um, se você precisar alterar a posição da linha em uma tabela de pedidos.

precisamos do valor $positionpara pesquisar a neighbourslinha Na minha tabela, criei uma coluna 'position'

e consulta SQL para obter a linha necessária é:

a seguir :

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

para anterior:

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

Se uma linha foi excluída, isso será interrompido. por exemplo: se os IDs são o número 1,2,6,7,8.
22430 Kevin Waterson

-2

Selecione o top 1 * de foo em que id> 4 ordena por id asc


1
O MySQL equivalente é "LIMIT 1": select * from foo where id>4 order by id asc LIMIT 1
mob
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.