MySQL'de sonraki / önceki kayıt nasıl alınır?


129

Diyelim ki 3,4,7,9 kimlikli kayıtlarım var ve sonraki / önceki bağlantılar aracılığıyla gezinerek birinden diğerine gidebilmek istiyorum. Sorun şu ki, en yakın yüksek kimliğe sahip kaydı nasıl getireceğimi bilmiyorum.

Dolayısıyla, kimliği 4 olan bir kaydım olduğunda, sonraki mevcut kaydı getirebilmem gerekiyor, bu da 7 olacaktır. Sorgu muhtemelen şuna benzer

SELECT * FROM foo WHERE id = 4 OFFSET 1

Tüm sonuç kümesini getirmeden ve manuel olarak yinelemeden sonraki / önceki kaydı nasıl getirebilirim?

MySQL 5 kullanıyorum.


8
Sıralama için kimliklere güvenmemelisiniz (yani: kimliğinizin bir auto_increment sütunu olduğunu varsaymak). Tabloda, sıralama düzenini açıkça gösteren başka bir sütun oluşturmalısınız.
İyi Dabbler

Yanıtlar:


258

Sonraki:

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

önceki:

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

7
Bence alt sorgular (id> 4 olduğunda foo'dan min (id) seçin) ve (id <4 olan foo'dan max (id) seçin).
İyi Dabbler

1
haklısın, FROM cümlesini unuttum. işaret ettiğiniz için teşekkürler. :)
longneck

2
Bu bir çözüm değildir çünkü satırı "foo" dan kaldırırsanız ne olur? : P
Valentin Hristov

@valentinhristov Ne dediğini anlamıyorum. Lütfen bir örnek verin.
longneck

@longneck Kaydettikten sonra yayınlarsanız her şey yolunda. Ancak, sürüm kimliğinden sonra içerik yayınlarsanız, sıradaki konum için kriter değildir.
Valentin Hristov

140

Cemkalyoncu'nun çözümüne ek olarak :

sonraki kayıt:

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

önceki kayıt:

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

düzenleme: Bu yanıt son zamanlarda birkaç olumlu oy aldığından, daha önce yaptığım yorumu vurgulamak istiyorum, birincil anahtar sütununun sıralama için bir sütun olmadığını anlamak için, çünkü MySQL daha yüksek, otomatik artırılmış değerler mutlaka daha sonra eklenir.

Bunu umursamıyorsanız ve daha yüksek (veya daha düşük) bir kayda ihtiyacınız varsa, ido zaman bu yeterli olacaktır. Bunu, bir kaydın gerçekten daha sonra (veya daha önce) eklenip eklenmediğini belirlemek için bir araç olarak kullanmayın. Bunun yerine, örneğin sıralama için bir tarih saat sütunu kullanmayı düşünün.


Bu cevabı daha çok tercih ederim, zaman damgasına göre sıralarım ve bu alt sorgu olmadığı anlamına gelir, yani 4 yerine toplam 2 sorgu.
Maurice

61

Yukarıdaki tüm çözümler iki veritabanı çağrısı gerektirir. Aşağıdaki sql kodu, iki sql ifadesini birde birleştirir.

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
Kesinlikle en iyi çözüm. Hızlı ve hepsi tek bir sorguda.
arka plan programı

Çok daha karmaşık bir sorguda bile mükemmel çalışır!
taiar

Ekleme DISTINCTönce *daha iyi olduğunu, tabii eğer yapacak idolabilir 0değeri olarak.
Ejaz

Mükemmel çözüm. İlk ve son kayıtta idiki yerine tekli olarak dönecektir .
lubosdz

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

1
+1 Bence bu en temiz çözüm. Ancak LIMIT cümlesi
sonda

ve SELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1önceki kayıt için
fwonce

12

Buna benzer bir şey yapmaya çalışıyordum, ancak tarihe göre sıralanan sonuçlara ihtiyacım vardı çünkü kimlik alanına sıralanabilir bir sütun olarak güvenemiyorum. İşte bulduğum çözüm.

İlk olarak, istediğimiz gibi sıralandığında, tablodaki istenen kaydın dizinini buluruz:

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;

Ardından sonucu 2 düşürerek limit ifadesine koyun. Örneğin, yukarıdaki benim için 21 döndürdü, bu yüzden koşuyorum:

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

Belirtilen siparişe göre size birincil kaydınızın yanı sıra sonraki ve önceki kayıtlarını verir.

Bunu tek bir veritabanı çağrısı olarak yapmaya çalıştım, ancak bir değişkeni parametrelerinden biri olarak almak için LIMIT ifadesini alamadım.


Yaklaşımınızla ilgileniyorum. Ya ayrıca bir JOIN yapmanız gerekirse? Diyelim ki makalelerden a JOIN yazarlar b ÜZERİNE b. This = a.that. Bana öyle geliyor ki, JOIN siparişi bozuyor. İşte bir örnek pastebin.com/s7R62GYV
idrarig

1
Bir JOIN yapmak için, iki farklı @ değişken tanımlamanız gerekir. `Geçerli.row, current.id, previous.row, previous.id SEÇİN (SELECT @rownum: = @ rownum + 1 satır, a. * A. Makalelerden (SELECT @rownum: = 0) r SİPARİŞ TARİH, id) olarak current_row LEFT JOIN (SELECT @ rownum2: = @ rownum2 + 1 satır, a. * makaleler a'dan, (SELECT @ rownum2: = 0) r SİPARİŞ TARİH, id) previous_row AÇIK olarak (current_row.id = previous_row. id) AND (current_row.row = previous_row.row -1) `
Eduardo Moralles

7

Bu örneği deneyin.

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; 

Not: Eğer değer bulunmazsa o zaman dönecektir null adlı .

Yukarıdaki örnekte, Önceki değer Raja olacak ve Sonraki değer, sonraki değer olmadığından boş olacaktır .


7

@Dan'ın yaklaşımını kullanarak, JOIN'ler oluşturabilirsiniz. Her alt sorgu için farklı bir @ değişken kullanın.

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)

Tam ihtiyacım olan şey, teşekkürler. Seçiminizin doğru olmadığını unutmayın, current-> current_row& previous-> previous_row.
Ludovic Guillaume

Bu sorgu, kayıtların tablodan silinip silinmediğini (auto_increment id sütununun sıralaması bozuluyor) bulmak için yararlı olabilir.
Dharmang

@LudovicGu Guillaume daha spesifik olabilir misin? Sözünüzü anlamadım üzgünüm. Ancak sorgunun previous_row içinde hiçbir öğesi yok. *
Walter Schrabmair

4

Sonraki satır

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

Önceki satır

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

sonraki satırda örnek

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

önceki satırı örnekle

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 ile aynı sorunu yaşadım, bu yüzden cevabını kullandım ve iyileştirdim.

Önce satır dizinini seçin, burada farklı bir şey yok.

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;

Şimdi iki ayrı sorgu kullanın. Örneğin satır dizini 21 ise, sonraki kaydı seçme sorgusu şöyle olacaktır:

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

Önceki kaydı seçmek için bu sorguyu kullanın:

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

İlk satır için (satır indeksi 1), sınırın -1'e gideceğini ve bir MySQL hatası alacağınızı unutmayın. Bunu önlemek için bir if ifadesi kullanabilirsiniz. Zaten önceden kayıt olmadığından hiçbir şey seçmeyin. Son kayıt durumunda, bir sonraki satır olacak ve bu nedenle sonuç alınmayacaktır.

Ayrıca, sipariş vermek için ASC yerine DESC kullanırsanız, sonraki ve önceki satırları seçme sorgularınızın hala aynı olduğunu, ancak değiştirildiğini unutmayın.


3

Bu, aynı sonuçlara sahip koşullar için evrensel bir çözümdür.

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

Burada, tek MySQL sorgusu kullanarak önceki ve sonraki kayıtları getirmenin bir yolu var. 5, geçerli kaydın kimliğidir.

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 ve PHP'de sonraki / önceki kayıt nasıl alınır?

Örneğim sadece kimliği almak

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'a açık olduğu için olumsuz oy verildi . Bunun eski bir gönderi olduğunu biliyorum, ancak acemi geliştiriciler bunun farkında olmalı.
ayrtonvwf

2

@Don yaklaşımını yalnızca Tek Sorgu kullanacak şekilde optimize etme

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'yi şu anki makale kimliğiyle değiştirin

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

Şimdiye kadar burada bulduğum en faydalı cevap bu. Önerdiğim tek gerçek iyileştirme, değişkenleri kullanmak ve tüm benzersiz parçaları kolayca düzenlenebilir olması için en üste taşımaktır. Daha sonra sıklıkla başvurulan bir işlev veya işlem haline getirilebilir.
liljoshu

1

@Row hilesine benzer bir değişken kullanarak, istediğiniz herhangi bir sıralamayı kullanarak önceki satırlardan sütunları göstermek için kullanabileceğiniz başka bir numara daha var:

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

Görünüşe göre, seçilen sütunlar sırayla değerlendirilir, bu nedenle bu önce kaydedilen değişkenleri seçecek ve ardından değişkenleri yeni satıra güncelleyecektir (işlemde onları seçerek).

NB: Bunun tanımlanmış davranış olduğundan emin değilim, ama kullandım ve işe yarıyor.


1

İsterseniz birden fazla beslemekid sorgunuzla ve almak next_idhepsi için ...

cur_idSeçim alanınıza atayın ve ardından next_idseçim alanının içine giren alt sorguya besleyin . Ve sonra sadece seçin next_id.

Hesaplamak için uzun cevap kullanma 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'a hoş geldiniz. Tamamen koddan oluşan yanıtlar nadiren faydalıdır. Lütfen kodunuzun ne yaptığına dair bazı açıklamalar sunmayı düşünün. Kod içi belgeler ve / veya İngilizce değişken adları da yardımcı olacaktır.
Politank-Z

1

Bir indeks sütununuz varsa, id diyelim, bir sql isteğinde önceki ve sonraki kimliği döndürebilirsiniz. ID değerini değerinizle değiştirin

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

Sonuncuysam ve tam tersi olursam ilk kayda geri dönmek için bir sonraki ve ön izleme kaydını almak için çözümüm

Kimliği kullanmıyorum, başlığı güzel url'ler için kullanıyorum

Codeigniter HMVC kullanıyorum

$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'a açık olduğu için olumsuz oy verildi . Bunun eski bir gönderi olduğunu biliyorum, ancak acemi geliştiriciler bunun farkında olmalı.
ayrtonvwf

0

İşte cevabım. ' Decent Dabbler'dan bir fikir alıyorum ve kimliğin min (id) ile max (id) arasında olup olmadığını kontrol eden kısmı ekliyorum . İşte masamı oluşturmanın parçası.

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

);

Sonraki adım, önceki kimliği almaktan sorumlu bir saklı yordam oluşturmaktır.

    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

İlk yöntem, dizinler sıralanırsa, ancak sıralanmamışsa iyidir. Örneğin, indeksleriniz varsa: 1,2,7 ve başka bir yaklaşım kullanmak için bu şekilde çok daha iyi indeks numarası 2 almanız gerekiyorsa.

    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
Büyücü rozeti mi arıyorsunuz? Bu sorunun 10 yıldır kabul gören bir cevabı var.
Jakub Arnold

Bu konuyu yeni buldum çünkü son zamanlarda aynı sorunu yaşıyorum.
Dennis

0

% 100 çalışıyor

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

-1

Bence SQL tablosunda gerçek bir sonraki veya önceki satırın gerçek değerine eşit olması gerekir, (<veya>) bir sıralama tablosunda satırın konumunu değiştirmeniz gerekirse birden fazla döndürür.

$positionaramak için değere ihtiyacımız varneighbourssatırı Tablomda bir sütun 'konum' oluşturdum

ve gerekli satırı almak için SQL sorgusu:

sonraki için:

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

önceki için:

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

Bir satır silinmişse, bu kesilir. örneğin: Kimlikler 1,2,6,7,8 numaraysa.
Kevin Waterson

-2

Foo'dan en iyi 1 * 'i seçin.


1
Eşdeğer MySQL "LIMIT 1": select * from foo where id>4 order by id asc LIMIT 1
mob
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.