Làm thế nào để có được bản ghi tiếp theo / trước trong MySQL?


129

Giả sử tôi có hồ sơ với ID 3,4,7,9 và tôi muốn có thể đi từ nơi này sang nơi khác bằng cách điều hướng qua các liên kết tiếp theo / trước. Vấn đề là, tôi không biết cách lấy bản ghi với ID cao hơn gần nhất.

Vì vậy, khi tôi có một bản ghi với ID 4, tôi cần có thể tìm nạp bản ghi hiện tại tiếp theo, đó là 7. Truy vấn có thể trông giống như

SELECT * FROM foo WHERE id = 4 OFFSET 1

Làm cách nào tôi có thể tìm nạp bản ghi tiếp theo / trước mà không tìm nạp toàn bộ tập kết quả và lặp lại theo cách thủ công?

Tôi đang sử dụng MySQL 5.


8
Bạn không nên dựa vào ID để sắp xếp (nghĩa là: giả sử ID của bạn là cột auto_increment). Bạn nên tạo một cột khác trong bảng mô tả rõ ràng thứ tự sắp xếp.
Dabbler Decent

Câu trả lời:


258

kế tiếp:

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

Trước:

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

7
Tôi nghĩ các truy vấn con nên là (chọn min (id) từ foo trong đó id> 4) và (chọn max (id) từ foo trong đó id <4).
Dabbler Decent

1
bạn nói đúng, tôi đã quên mệnh đề TỪ. Cảm ơn đã chỉ ra điều đó. :)
longneck

2
Đây không phải là giải pháp vì nếu bạn xóa hàng khỏi "foo", điều gì sẽ xảy ra? : P
Valentin Hristov

@valentinhristov Tôi không hiểu bạn đang nói gì. Vui lòng cung cấp một ví dụ.
longneck

@longneck Nếu bạn đăng bài sau khi ghi thì mọi thứ đều ổn. Nhưng nếu bạn đăng nội dung sau id phiên bản không phải là tiêu chí cho vị trí theo thứ tự.
Valentin Hristov ngày

140

Ngoài giải pháp của cemkalyoncu :

kỷ lục tiếp theo:

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

hồ sơ trước:

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

chỉnh sửa: Vì câu trả lời này đã nhận được một vài câu hỏi gần đây, tôi thực sự muốn nhấn mạnh nhận xét mà tôi đã đưa ra trước đó về việc hiểu rằng một khóa chính không có nghĩa là một cột để sắp xếp, bởi vì MySQL không đảm bảo rằng mức tăng tự động cao hơn , các giá trị nhất thiết phải được thêm vào sau đó.

Nếu bạn không quan tâm đến điều này và chỉ cần bản ghi với mức cao hơn (hoặc thấp hơn) idthì điều này sẽ đủ. Chỉ không sử dụng điều này như một phương tiện để xác định xem một bản ghi thực sự được thêm vào sau (hoặc sớm hơn). Thay vào đó, hãy xem xét sử dụng cột datetime để sắp xếp theo, ví dụ.


Tôi rất thích câu trả lời này, tôi sắp xếp theo dấu thời gian và nó có nghĩa là không có truy vấn con, vì vậy tổng số 2 truy vấn thay vì 4.
Maurice

61

Tất cả các giải pháp trên yêu cầu hai cuộc gọi cơ sở dữ liệu. Mã sql dưới đây kết hợp hai câu lệnh sql thành một.

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
Chắc chắn là giải pháp tốt nhất. Nhanh chóng và tất cả trong một truy vấn.
daemon

Hoạt động hoàn hảo ngay cả trong một truy vấn phức tạp hơn nhiều!
taiar

Thêm DISTINCTtrước *sẽ làm cho nó thậm chí tốt hơn, đó là, tất nhiên nếu idcó thể có 0giá trị.
Ejaz

Giải pháp tuyệt vời. Trên bản ghi đầu tiên và cuối cùng sẽ trả về đơn idhơn là hai.
Lubosdz

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

1
+1 Tôi nghĩ rằng đây là giải pháp sạch nhất. Nhưng mệnh đề LIMIT sẽ xuất hiện sau cùng: CHỌN * TỪ foo WHERE id> 4 ĐẶT HÀNG
B idNG id GIỚI

SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1cho hồ sơ previouse
fwonce

12

Tôi đã cố gắng làm một cái gì đó tương tự như thế này, nhưng tôi cần các kết quả được sắp xếp theo ngày vì tôi không thể dựa vào trường ID như một cột có thể sắp xếp. Đây là giải pháp tôi đã đưa ra.

Đầu tiên chúng ta tìm ra chỉ mục của bản ghi mong muốn trong bảng, khi nó được sắp xếp theo ý muốn:

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;

Sau đó giảm kết quả bằng 2 đặt nó trong câu lệnh giới hạn. Ví dụ, trả lại 21 cho tôi để tôi chạy:

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

Cung cấp cho bạn hồ sơ chính của bạn cùng với hồ sơ tiếp theo và trước đó cho đơn đặt hàng đã nêu của bạn.

Tôi đã cố gắng thực hiện nó như một cuộc gọi cơ sở dữ liệu duy nhất, nhưng không thể nhận được câu lệnh LIMIT để lấy một biến làm một trong các tham số của nó.


Tôi quan tâm đến cách tiếp cận của bạn. Điều gì nếu bạn cần phải làm THAM GIA? Nói, TỪ bài viết một tác giả THAM GIA b ON b.this = a.that. Dường như với tôi rằng THAM GIA làm rối trật tự. Dưới đây là một ví dụ pastebin.com/s7R62GYV
idrarig

1
Để thực hiện THAM GIA, bạn cần xác định hai biến khác nhau. `CHỌN current.row, current.id, trước.row, trước.id TỪ (SELECT @rownum: = @ rownum + 1 hàng, a. * TỪ các bài viết a, (SELECT @rownum: = 0) r ĐẶT HÀNG theo ngày, id) as current_row LEFT THAM GIA (SELECT @ rownum2: = @ rownum2 + 1 hàng, a. * TỪ các bài viết a, (SELECT @ rownum2: = 0) r ĐẶT HÀNG theo ngày, id) như trước_row ON (current_row.id = trước_row. id) AND (current_row.row =
trước_row.row

7

Hãy thử ví dụ này.

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; 

Lưu ý: Nếu không tìm thấy giá trị thì nó sẽ trả về null .

Trong ví dụ trên, giá trị trước sẽ là Raja và giá trị tiếp theo sẽ là null vì không có giá trị tiếp theo.


7

Sử dụng phương pháp của @Dan, bạn có thể tạo THAM GIA. Chỉ cần sử dụng một @variable khác nhau cho mỗi truy vấn phụ.

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)

Chỉ cần những gì tôi cần, cảm ơn. Lưu ý rằng lựa chọn của bạn không chính xác, current-> current_row& previous-> previous_row.
Ludovic Guillaume

Truy vấn này có thể hữu ích để tìm xem liệu các bản ghi đã bị xóa khỏi bảng (chuỗi cột id auto_increment có bị phá vỡ hay không).
Dharmang

@LudovicGuillaume bạn có thể cụ thể hơn. Tôi không hiểu nhận xét của bạn, xin lỗi. Nhưng truy vấn không có thành phần nào trong
trước_row

4

Hàng tiếp theo

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

Hàng trước

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

mẫu hàng tiếp theo

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

mẫu hàng trước

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

Tôi có cùng một vấn đề như Dan, vì vậy tôi đã sử dụng câu trả lời của anh ấy và cải thiện nó.

Đầu tiên chọn chỉ số hàng, không có gì khác nhau ở đây.

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;

Bây giờ sử dụng hai truy vấn riêng biệt. Ví dụ: nếu chỉ mục hàng là 21, truy vấn để chọn bản ghi tiếp theo sẽ là:

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

Để chọn bản ghi trước, sử dụng truy vấn này:

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

Hãy nhớ rằng đối với hàng đầu tiên (chỉ số hàng là 1), giới hạn sẽ là -1 và bạn sẽ gặp lỗi MySQL. Bạn có thể sử dụng một câu lệnh if để ngăn chặn điều này. Chỉ cần không chọn bất cứ điều gì, vì dù sao không có hồ sơ trước. Trong trường hợp của bản ghi cuối cùng, sẽ có hàng tiếp theo và do đó sẽ không có kết quả.

Ngoài ra, hãy nhớ rằng nếu bạn sử dụng DESC để đặt hàng, thay vì ASC, bạn truy vấn để chọn các hàng tiếp theo và trước đó vẫn giống nhau, nhưng đã được chuyển đổi.


3

Đây là giải pháp phổ quát cho các điều kiện với nhiều kết quả tương tự.

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

Ở đây chúng tôi có một cách để tìm nạp các bản ghi trước đó và tiếp theo bằng một truy vấn MySQL duy nhất. Trong đó 5 là id của hồ sơ hiện tại.

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

Làm thế nào để có được bản ghi tiếp theo / trước trong MySQL & PHP?

Ví dụ của tôi là chỉ lấy 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;
  }
}

Bị từ chối vì nó dễ bị SQL Injection . Tôi biết đây là một bài viết cũ, nhưng các nhà phát triển mới làm quen nên biết về nó.
ayrtonvwf

2

Tối ưu hóa cách tiếp cận @Don chỉ sử dụng một Truy vấn

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

thay đổi CurrentArticleID với ID bài viết hiện tại như

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

Đây là câu trả lời hữu ích nhất mà tôi đã tìm thấy ở đây cho đến nay. Chỉ cải tiến thực sự mà tôi đề xuất là sử dụng các biến và di chuyển tất cả các phần duy nhất lên trên cùng để có thể dễ dàng chỉnh sửa. Sau đó có thể dễ dàng được thực hiện thành một chức năng hoặc quy trình thường được tham chiếu.
liljoshu

1

Có một mẹo khác mà bạn có thể sử dụng để hiển thị các cột từ các hàng trước đó, sử dụng bất kỳ thứ tự nào bạn muốn, sử dụng một biến tương tự như thủ thuật @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

Rõ ràng, các cột chọn được đánh giá theo thứ tự, do đó, trước tiên sẽ chọn các biến đã lưu và sau đó cập nhật các biến sang hàng mới (chọn chúng trong quy trình).

NB: Tôi không chắc chắn rằng đây là hành vi được xác định, nhưng tôi đã sử dụng nó và nó hoạt động.


1

Nếu bạn muốn cung cấp nhiều hơn mộtid cho truy vấn của mình và nhận next_idtất cả chúng ...

Chỉ định cur_idtrong trường chọn của bạn và sau đó đưa nó vào truy vấn con next_idbên trong trường chọn. Và sau đó chọn chỉ next_id.

Sử dụng câu trả lời longneck cho 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
Chào mừng bạn đến với StackOverflow. Câu trả lời bao gồm toàn bộ mã hiếm khi hữu ích. Vui lòng xem xét việc cung cấp một số giải thích về những gì mã của bạn đang làm. Tài liệu trong mã và / hoặc tên biến trong tiếng Anh cũng sẽ hữu ích.
Politank-Z

1

Nếu bạn có một cột chỉ mục, giả sử id, bạn có thể trả về id trước và id tiếp theo trong một yêu cầu sql. Thay thế: id bằng giá trị của bạn

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

Giải pháp của tôi để có được bản ghi tiếp theo và bản xem trước cũng để quay lại bản ghi đầu tiên nếu tôi là người cuối cùng và ngược lại

Tôi không sử dụng id tôi đang sử dụng tiêu đề cho url đẹp

Tôi đang sử dụng 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;
    }     
}

Bị từ chối vì nó dễ bị SQL Injection . Tôi biết đây là một bài viết cũ, nhưng các nhà phát triển mới làm quen nên biết về nó.
ayrtonvwf

0

Đây là câu trả lời của tôi. Tôi lấy một ý tưởng từ ' Decab Dabbler ' và thêm phần đang kiểm tra xem id có nằm giữa min (id) và max (id) không. Đây là phần để tạo bảng của tôi.

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

);

Bước tiếp theo là tạo một thủ tục được lưu trữ chịu trách nhiệm lấy id trước đó.

    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

Phương pháp đầu tiên là tốt nếu các chỉ mục được sắp xếp, nhưng nếu chúng không. Ví dụ: nếu bạn có chỉ mục: 1,2,7 và bạn cần lấy chỉ số số 2 theo cách này tốt hơn nhiều để sử dụng phương pháp khác.

    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
Săn lùng huy hiệu necromancer? Câu hỏi này đã có một câu trả lời được chấp nhận trong 10 năm.
Jakub Arnold

Tôi chỉ tìm thấy chủ đề này, bởi vì tôi đã gặp vấn đề tương tự gần đây.
Dennis

0

Làm việc 100%

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

-1

Tôi nghĩ để có hàng thực sự tiếp theo hoặc trước đó trong bảng SQL, chúng ta cần giá trị thực bằng nhau, (<hoặc>) trả về nhiều hơn một nếu bạn cần thay đổi vị trí của hàng trong bảng đặt hàng.

chúng ta cần giá trị $positionđể tìm kiếm neighbourshàng Trong bảng của tôi, tôi đã tạo một cột 'vị trí'

và truy vấn SQL để nhận hàng cần thiết là:

cho tiếp theo :

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

cho trước đó:

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

Nếu một hàng đã bị xóa, điều này sẽ phá vỡ. ví dụ: nếu ID là số 1,2,6,7,8.
Kevin Waterson

-2

Chọn top 1 * từ foo trong đó id> 4 thứ tự theo id asc


1
MySQL tương đương là "GIỚI HẠN 1": select * from foo where id>4 order by id asc LIMIT 1
mob
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.