MySQL에서 다음 / 이전 기록을 얻는 방법?


129

ID가 3,4,7,9 인 레코드가 있고 다음 / 이전 링크를 통해 탐색하여 서로간에 이동할 수 있기를 원한다고 가정합니다. 문제는 가장 높은 ID로 레코드를 가져 오는 방법을 모른다는 것입니다.

따라서 ID 4의 레코드가있는 경우 기존 레코드 인 7을 가져올 수 있어야합니다. 쿼리는 다음과 같습니다.

SELECT * FROM foo WHERE id = 4 OFFSET 1

전체 결과 세트를 가져 오지 않고 수동으로 반복하지 않고 다음 / 이전 레코드를 가져 오는 방법은 무엇입니까?

MySQL 5를 사용하고 있습니다.


8
정렬을 위해 ID에 의존해서는 안됩니다 (즉, ID가 auto_increment 열이라고 가정). 정렬 순서를 명시 적으로 나타내는 다른 열을 테이블에 작성해야합니다.
알맞은 Dabbler

답변:


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
하위 쿼리는 (id> 4 인 경우 foo에서 min (id)를 선택하고, id <4 인 경우 foo에서 max (id)를 선택하십시오)라고 생각합니다.
Decent Dabbler

1
당신 말이 맞아요, 난 FROM 절을 잊어 버렸습니다. 지적 해 주셔서 감사합니다. :)
longneck

2
"foo"에서 행을 제거하면 어떻게됩니까? : P
Valentin Hristov 2013

@valentinhristov 나는 당신이 무슨 말을하는지 이해하지 못합니다. 예를 들어주세요.
longneck February

@longneck 녹음 후 게시하면 모든 것이 정상입니다. 그러나 버전 ID 이후에 콘텐츠를 게시하는 경우 순서대로 게재 순위에 대한 기준이 아닙니다.
Valentin Hristov

140

셈 칼리 온쿠 외에도 솔루션 :

다음 기록 :

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

이전 기록 :

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

편집 : 이 답변은 최근에 몇 가지 upvotes를 받고 있기 때문에, 나는 내가 한 의견 을 강조하고 싶습니다 으므로 MySQL은 기본 키 열이 정렬 할 열로 의미가 없다는 것을 이해하기 위해 앞서 .MySQL은 더 높은 자동 증가를 보장하지 않기 때문입니다. 값은 나중에 추가해야합니다.

이것에 신경 쓰지 않고 단순히 더 높은 (또는 더 낮은) 레코드가 필요하다면 id이것으로 충분합니다. 레코드가 실제로 나중에 추가되는지 또는 이전에 추가되는지 여부를 판별하기위한 수단으로 이것을 사용하지 마십시오. 대신 날짜 시간 열을 사용하여 정렬하는 것을 고려하십시오.


이 답변을 선호합니다. 타임 스탬프별로 정렬하며 하위 쿼리가 없으므로 4 개 대신 총 2 개의 쿼리가 필요합니다.
Maurice

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
확실히 최고의 솔루션입니다. 하나의 쿼리로 빠르고 모두.
daemon

훨씬 더 복잡한 쿼리에서도 완벽하게 작동합니다!
taiar

DISTINCT전에 추가하면 *더 좋을 것입니다. 물론 가치 id가 있을 수 있습니다 0.
Ejaz

탁월한 솔루션. 첫 번째 및 마지막 레코드에서 id두 개가 아닌 단일 레코드를 반환 합니다.
lubosdz

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

1
+1 이것이 가장 깨끗한 해결책이라고 생각합니다. 그러나 LIMIT 절은 마지막으로 와야한다 : foo는 SELECT * FROM을 WHERE ID의 LIMIT 1 BY ID> 4 ORDER
점잖은 물장난

SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1previouse 레코드
fwonce

12

이와 비슷한 작업을 시도했지만 ID 필드를 정렬 가능한 열로 사용할 수 없으므로 날짜별로 정렬 된 결과가 필요했습니다. 내가 생각해 낸 해결책은 다음과 같습니다.

먼저 원하는 정렬 순서대로 테이블에서 원하는 레코드의 인덱스를 찾습니다.

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 씩 줄이면 limit 문에 넣습니다. 예를 들어 위의 내용은 21을 반환하므로 다음을 실행합니다.

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

명시된 순서대로 다음 레코드 및 이전 레코드와 함께 기본 레코드를 제공합니다.

단일 데이터베이스 호출로 시도했지만 매개 변수 중 하나로 변수를 사용하는 LIMIT 문을 가져올 수 없습니다.


나는 당신의 접근 방식에 관심이 있습니다. 가입해야 할 경우 어떻게해야합니까? FROM 기사 a JOIN 저자 b b.this = a.that. JOIN이 주문을 망쳐 놓은 것으로 보입니다. 다음은 pastebin.com/s7R62GYV
idrarig

1
JOIN을 수행하려면 서로 다른 두 @ 변수를 정의해야합니다. `SELECT current.row, current.id, previous.row, previous.id FROM (SELECT @rownum : = @ rownum + 1 행, a. * FROM 기사 a, (SELECT @rownum : = 0) r ORDER BY 날짜, id) as current_row LEFT JOIN (SELECT @ rownum2 : = @ rownum2 + 1 행, a. * FROM 기사 a, (SELECT @ rownum2 : = 0) r ORDER BY 날짜, id) previous_row ON (current_row.id = previous_row) id) AND (current_row.row = previous_row.row -1)`
Eduardo Moralles

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; 

참고 : 경우 값이 발견되지 후 반환됩니다 널 (null)을 .

위의 예에서 이전 값은 Raja 이고 다음 값 은 없으므로 다음 값은 null 입니다.


7

@Dan의 접근 방식을 사용하여 JOIN을 만들 수 있습니다. 각 하위 쿼리마다 다른 @ 변수를 사용하십시오.

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.
루도빅 기 illa

이 쿼리는 테이블에서 레코드가 삭제되었는지 (auto_increment id 열의 순서가 끊어 졌는지) 여부를 찾는 데 유용 할 수 있습니다.
달망

@LudovicGuillaume 더 구체적으로 할 수 있습니다. 나는 당신의 말을 이해하지 못합니다. 죄송합니다. 그러나이 쿼리에는 previous_row에 요소가 없습니다. *
Walter Schrabmair

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

나는 Dan과 같은 문제가 있었으므로 그의 대답을 사용하고 개선했습니다.

먼저 행 인덱스를 선택하십시오.

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 문을 사용할 수 있습니다. 어쨌든 이전 기록이 없으므로 아무것도 선택하지 마십시오. 마지막 레코드의 경우 다음 행이 있으며 결과가 없습니다.

또한 ASC 대신 DESC를 사용하여 주문하면 다음 행과 이전 행을 선택하기위한 쿼리는 여전히 동일하지만 전환됩니다.


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는 현재 레코드의 ID입니다.

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에서 다음 / 이전 기록을 얻는 방법?

내 예는 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;
  }
}

SQL Injection에 취약하기 때문에 다운 보트되었습니다 . 나는 이것이 오래된 게시물이라는 것을 알고 있지만 초보자 개발자는 알고 있어야합니다.
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를 다음과 같은 현재 기사 ID로 변경하십시오.

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.

calc에 longneck 응답 사용 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를 반환 할 수 있습니다. : 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의 제목을 사용하고있는 ID를 사용하고 있지 않습니다.

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 Injection에 취약하기 때문에 다운 보트되었습니다 . 나는 이것이 오래된 게시물이라는 것을 알고 있지만 초보자 개발자는 알고 있어야합니다.
ayrtonvwf

0

여기 내 대답이 있습니다. ' Decent Dabbler ' 에서 아이디어를 가져 와서 id 가 min (id)와 max (id) 사이 인지 확인하는 부분을 추가하십시오 . 여기 내 테이블을 만드는 부분이 있습니다.

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

);

다음 단계는 이전 ID를 가져 오는 저장 프로 시저를 만드는 것입니다.

    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 년 동안 받아 들여진 대답이었습니다.
Jakub Arnold

최근에 같은 문제가 발생했기 때문에이 주제를 찾았습니다.
Dennis

0

100 % 작동

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

-1

SQL 테이블에 실제 다음 또는 이전 행이 있다고 생각합니다. 순서 테이블에서 행의 위치를 ​​변경 해야하는 경우 동일한 값을 가진 실제 값이 (<또는>) 두 개 이상 반환되어야한다고 생각합니다.

$position검색 할 가치가 필요 합니다neighbours행 . 내 테이블에서 'position'열을 만들었습니다.

필요한 행을 얻는 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

행이 삭제되면 끊어집니다. 예 : ID가 1,2,6,7,8 인 경우
Kevin Waterson 17

-2

id asc로 id> 4 order 인 foo에서 top 1 *를 선택하십시오.


1
이에 상응하는 MySQL은 "LIMIT 1"입니다. select * from foo where id>4 order by id asc LIMIT 1
mob
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.