Bagaimana cara mendapatkan catatan berikutnya / sebelumnya di MySQL?

129

Katakanlah saya memiliki catatan dengan ID 3,4,7,9 dan saya ingin dapat berpindah dari satu ke yang lain dengan navigasi melalui tautan berikutnya / sebelumnya. Masalahnya adalah, saya tidak tahu cara mengambil rekaman dengan ID tertinggi terdekat.

Jadi, ketika saya memiliki catatan dengan ID 4, saya harus dapat mengambil catatan berikutnya yang ada, yang akan menjadi 7. Kueri mungkin akan terlihat seperti

SELECT * FROM foo WHERE id = 4 OFFSET 1

Bagaimana saya bisa mengambil catatan berikutnya / sebelumnya tanpa mengambil seluruh set hasil dan iterasi secara manual?

Saya menggunakan MySQL 5.

Jakub Arnold
sumber
8
Anda tidak harus bergantung pada ID untuk menyortir (yaitu: menganggap ID Anda adalah kolom auto_increment). Anda harus membuat kolom lain di tabel yang secara eksplisit menggambarkan urutan pengurutan.
Decent Dabbler

Jawaban:

258

lanjut:

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

sebelumnya:

select * from foo where id = (select max(id) from foo where id < 4)
leher panjang
sumber
7
Saya pikir subqueries harus (pilih min (id) dari foo mana id> 4) dan (pilih maks (id) dari foo di mana id <4).
Decent Dabbler
1
Anda benar, saya lupa klausa FROM. terima kasih telah menunjukkannya. :)
longneck
2
Ini bukan solusi karena jika Anda menghapus baris dari "foo", apa yang akan terjadi? : P
Valentin Hristov
@valentinhristov Saya tidak mengerti apa yang Anda katakan. Tolong berikan contoh.
longneck
@longneck Jika Anda memposting setelah merekam semuanya baik-baik saja. Tetapi jika Anda memposting konten setelah versi id bukan kriteria untuk posisi secara berurutan.
Valentin Hristov
140

Selain solusi cemkalyoncu :

catatan selanjutnya:

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

catatan sebelumnya:

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

sunting: Karena jawaban ini telah mendapatkan beberapa upvotes akhir-akhir ini, saya benar-benar ingin menekankan komentar yang saya buat sebelumnya tentang memahami bahwa colum kunci primer tidak dimaksudkan sebagai kolom untuk mengurutkan berdasarkan, karena MySQL tidak menjamin yang lebih tinggi, otomatis bertambah , nilai harus ditambahkan di lain waktu.

Jika Anda tidak peduli dengan ini, dan hanya perlu catatan dengan yang lebih tinggi (atau lebih rendah) idmaka ini sudah cukup. Hanya saja, jangan gunakan ini sebagai sarana untuk menentukan apakah suatu catatan benar-benar ditambahkan kemudian (atau lebih awal). Sebagai gantinya, pertimbangkan menggunakan kolom datetime untuk mengurutkan berdasarkan, misalnya.

Dabbler yang Layak
sumber
Saya lebih suka jawaban ini, saya urutkan berdasarkan stempel waktu, dan itu berarti tidak ada subquery, jadi totalnya 2 kueri, bukan 4.
Maurice
61

Semua solusi di atas membutuhkan dua panggilan basis data. Kode sql di bawah ini menggabungkan dua pernyataan sql menjadi satu.

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)
      )    
sudip
sumber
3
Pasti solusi terbaik. Cepat dan semuanya dalam satu kueri.
daemon
Bekerja perfeclty bahkan dalam permintaan yang jauh lebih rumit!
taiar
Menambahkan DISTINCTsebelum *akan membuatnya lebih baik, yaitu, tentu saja jika iddapat memiliki 0nilai.
Ejaz
Solusi yang sangat baik. Rekaman pertama & terakhir akan mengembalikan tunggal iddaripada dua.
lubosdz
19
SELECT * FROM foo WHERE id>4 ORDER BY id LIMIT 1
Cem Kalyoncu
sumber
1
+1 Saya pikir ini adalah solusi terbersih. Tetapi klausa LIMIT harus datang terakhir: PILIH * DARI foo WHERE id> 4 ORDER OLEH id LIMIT 1
Decent Dabbler
dan SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1untuk catatan previouse
fwonce
12

Saya mencoba melakukan sesuatu yang mirip dengan ini, tetapi saya membutuhkan hasil yang dipesan berdasarkan tanggal karena saya tidak dapat mengandalkan bidang ID sebagai kolom yang dapat diurutkan. Inilah solusi yang saya buat.

Pertama-tama kita mengetahui indeks catatan yang diinginkan dalam tabel, ketika itu diurutkan seperti yang kita inginkan:

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;

Kemudian kurangi hasilnya dengan 2 taruh dalam pernyataan batas. Misalnya di atas mengembalikan 21 untuk saya jadi saya jalankan:

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

Memberi Anda catatan utama Anda bersama dengan itu berikutnya dan catatan sebelumnya diberikan pesanan Anda.

Saya mencoba melakukannya sebagai panggilan database tunggal, tetapi tidak bisa mendapatkan pernyataan LIMIT untuk mengambil variabel sebagai salah satu parameter itu.

Dan
sumber
Saya tertarik dengan pendekatan Anda. Bagaimana jika Anda perlu melakukan GABUNG juga? Katakan, DARI artikel a GABUNG penulis b ON b.this = a.that. Tampak bagi saya bahwa GABUNGAN mengacaukan pemesanan. Berikut ini contoh pastebin.com/s7R62GYV
idrarig
1
Untuk melakukan GABUNG, Anda perlu mendefinisikan dua variabel yang berbeda. `SELECT current.row, current.id, prior.row, prior.id DARI (SELECT @rownum: = @ rownum + 1 baris, a. * DARI artikel a, (SELECT @rownum: = 0) r ORDER DENGAN tanggal, id) sebagai current_row LEFT BERGABUNG (SELECT @ rownum2: = @ rownum2 + 1 baris, a. * DARI artikel a, (SELECT @ rownum2: = 0) r PESANAN DENGAN tanggal, id) seperti before_row ON (current_row.id = before_row. id) AND (current_row.row =
prior_row.row
7

Coba contoh ini.

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; 

Catatan: Jika nilai tidak ditemukan maka akan mengembalikan nol .

Dalam contoh di atas, Nilai sebelumnya akan menjadi Raja dan nilai berikutnya akan menjadi nol karena tidak ada nilai berikutnya.

Ranga Reddy
sumber
7

Menggunakan pendekatan @Dan, Anda dapat membuat GABUNG. Cukup gunakan @variable yang berbeda untuk setiap sub kueri.

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
sumber
Apa yang saya butuhkan, terima kasih. Perhatikan bahwa pilihan Anda tidak benar, current-> current_row& previous-> previous_row.
Ludovic Guillaume
Kueri ini dapat berguna untuk menemukan apakah catatan dihapus dari tabel (urutan kolom id auto_increment rusak) atau tidak.
Dharmang
@LudovicGuillaume dapat Anda lebih spesifik. Saya tidak mengerti ucapan Anda, maaf. Tetapi kueri tidak memiliki elemen di
before_row
4

Baris berikutnya

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

Baris sebelumnya

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

contoh baris berikutnya

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

contoh baris sebelumnya

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

Saya memiliki masalah yang sama dengan Dan, jadi saya menggunakan jawabannya dan memperbaikinya.

Pertama pilih indeks baris, tidak ada yang berbeda di sini.

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;

Sekarang gunakan dua pertanyaan terpisah. Misalnya jika indeks baris adalah 21, kueri untuk memilih catatan berikutnya adalah:

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

Untuk memilih catatan sebelumnya, gunakan kueri ini:

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

Ingatlah bahwa untuk baris pertama (indeks baris adalah 1), batasnya akan menjadi -1 dan Anda akan mendapatkan kesalahan MySQL. Anda dapat menggunakan pernyataan if untuk mencegah hal ini. Hanya saja jangan memilih apa pun, karena bagaimanapun juga tidak ada catatan sebelumnya. Dalam hal catatan terakhir, akan ada baris berikutnya dan karenanya tidak akan ada hasil.

Juga perlu diingat bahwa jika Anda menggunakan DESC untuk memesan, alih-alih ASC, Anda kueri untuk memilih baris berikutnya dan sebelumnya masih sama, tetapi diaktifkan.

magnetronnie
sumber
3

Ini adalah solusi universal untuk kondisi dengan hasil yang lebih sama.

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

Di sini kita memiliki cara untuk mengambil catatan sebelumnya dan berikutnya menggunakan permintaan MySQL tunggal. Di mana 5 adalah id dari catatan saat ini.

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

Bagaimana cara mendapatkan catatan berikutnya / sebelumnya di MySQL & PHP?

Contoh saya adalah untuk mendapatkan id saja

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
sumber
Diturunkan karena rentan terhadap SQL Injection . Saya tahu ini adalah posting lama, tetapi pengembang pemula harus menyadarinya.
ayrtonvwf
2

Mengoptimalkan pendekatan @Don hanya menggunakan One Query

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

ubah CurrentArticleID dengan ID artikel saat ini seperti

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
sumber
Ini adalah jawaban paling berguna yang saya temukan di sini sejauh ini. Hanya perbaikan nyata yang saya sarankan menggunakan variabel dan memindahkan semua bagian unik ke atas agar mudah diedit. Kemudian dapat dengan mudah dibuat menjadi fungsi atau proses yang sering dirujuk.
liljoshu
1

Ada trik lain yang dapat Anda gunakan untuk menampilkan kolom dari baris sebelumnya, menggunakan pemesanan apa pun yang Anda inginkan, menggunakan variabel yang mirip dengan trik @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

Rupanya, kolom pilih dievaluasi secara berurutan, jadi ini pertama akan memilih variabel yang disimpan, dan kemudian memperbarui variabel ke baris baru (memilih mereka dalam proses).

NB: Saya tidak yakin ini perilaku yang didefinisikan, tetapi saya sudah menggunakannya dan berfungsi.

Willem M.
sumber
1

Jika Anda ingin memberi makan lebih dari satuid ke kueri Anda dan dapatkannext_id semuanya ...

Tetapkan cur_iddi bidang pilih Anda dan kemudian berikan ke subquery masuk ke next_iddalam bidang pilih. Dan kemudian pilih sajanext_id .

Menggunakan jawaban longneck untuk 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
sumber
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
sumber
6
Selamat datang di StackOverflow. Jawaban yang seluruhnya terdiri dari kode jarang bermanfaat. Harap pertimbangkan untuk memberikan penjelasan tentang apa yang dilakukan kode Anda. Dokumentasi dalam kode dan / atau nama variabel dalam bahasa Inggris juga akan membantu.
Politank-Z
1

Jika Anda memiliki kolom indeks, katakan id, Anda dapat mengembalikan id sebelumnya dan berikutnya dalam satu permintaan sql. Ganti: id dengan nilai Anda

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

Solusi saya untuk mendapatkan catatan berikutnya dan preview juga untuk kembali ke catatan pertama jika saya dengan yang terakhir dan sebaliknya

Saya tidak menggunakan id saya menggunakan judul untuk url yang bagus

Saya menggunakan 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
sumber
Diturunkan karena rentan terhadap SQL Injection . Saya tahu ini adalah posting lama, tetapi pengembang pemula harus menyadarinya.
ayrtonvwf
0

Inilah jawaban saya. Saya mengambil ide dari ' Decent Dabbler ' dan menambahkan bagian yang memeriksa apakah id antara min (id) dan max (id). Inilah bagian untuk membuat tabel saya.

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

);

Langkah selanjutnya adalah membuat prosedur tersimpan yang bertanggung jawab untuk mendapatkan id sebelumnya.

    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

Metode pertama baik jika indeks diurutkan, tetapi jika tidak. Misalnya, jika Anda memiliki indeks: 1,2,7 dan Anda harus mendapatkan nomor indeks 2 dengan cara ini, lebih baik menggunakan pendekatan lain.

    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
sumber
1
Berburu untuk lencana ahli nujum? Pertanyaan ini telah mendapat jawaban yang diterima selama 10 tahun.
Jakub Arnold
Saya baru saja menemukan topik ini, karena saya mendapat masalah yang sama baru-baru ini.
Dennis
0

100% berfungsi

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

Saya pikir untuk memiliki baris nyata berikutnya atau sebelumnya dalam tabel SQL kita perlu nilai nyata dengan sama, (<atau>) mengembalikan lebih dari satu jika Anda perlu mengubah posisi baris dalam tabel pemesanan.

kita perlu nilai $positionuntuk mencari neighboursbaris Di meja saya, saya membuat kolom 'posisi'

dan permintaan SQL untuk mendapatkan baris yang diperlukan adalah:

untuk selanjutnya :

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

untuk sebelumnya:

 SELECT * 
 FROM my_table 
 WHERE id = (SELECT (id) 
             FROM my_table 
             WHERE `position` = ($position-1)) 
 LIMIT 1
Roussos
sumber
Jika satu baris telah dihapus, ini akan rusak. misalnya: jika ID adalah nomor 1,2,6,7,8.
Kevin Waterson
-2

Pilih top 1 * dari foo mana id> 4 order oleh id asc

Gratzy
sumber
1
MySQL Setara adalah "LIMIT 1": select * from foo where id>4 order by id asc LIMIT 1
mob