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カラムであると想定します)。ソート順を明示的に表す別の列をテーブルに作成する必要があります。
まともなダブラー2009

回答:


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
サブクエリは(select min(id)from foo where id> 4)および(select max(id)from foo where id <4)である必要があると思います。
まともなダブラー2009

1
あなたが正しい、私はFROM句を忘れていました。指摘してくれてありがとう。:)
ロングネック2009

2
「foo」から行を削除すると、どうなるでしょうか?:P
Valentin Hristov 2014

@valentinhristovあなたの言っていることが理解できません。例を挙げてください。
ロングネック、2014

@longneck録音後に投稿すれば、すべて問題ありません。ただし、バージョンIDの後にコンテンツを投稿する場合は、順序での位置の基準にはなりません。
Valentin Hristov 2014年

140

cemkalyoncuのソリューションに加えて:

次のレコード:

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

前の記録:

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

編集:この回答は最近いくつかの賛成票を得ているので、MySQLはより高い自動インクリメントを保証しないため、主キーカラムは並べ替え用の列として意図されていないことを理解することについて先に行っコメントを強調したいと思います、値は必ず後で追加されます。

これを気にせず、単に高い(または低い)レコードが必要な場合はid、これで十分です。これを実際にレコードが実際に後(または前)に追加されたかどうかを判断する手段として使用しないでください。代わりに、たとえば、日時列を使用してソートすることを検討してください。


その2つのクエリが4の代わりに合計、私はずっと私の並べ替えタイムスタンプで、この答えを好む、それは何のサブクエリを意味しない
モーリス

61

上記のすべてのソリューションでは、2つのデータベース呼び出しが必要です。以下のSQLコードは、2つのSQLステートメントを1つに結合しています。

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
間違いなく最高のソリューション。1つのクエリで高速かつすべて。
デーモン2013年

より複雑なクエリでも完全に機能します。
taiar 2014

DISTINCTbefore *を追加することはそれをより良くします。つまり、もちろん値としてid持つことができる場合0です。
Ejaz

優れたソリューション。最初と最後のレコードidでは、2つではなく1つが返されます。
lubosdz 2017

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

1
+1これが最もクリーンなソリューションだと思います。しかし、LIMIT句は最後に来る必要があります:SELECT * FROM foo WHERE id> 4 ORDER BY id LIMIT 1
Decent Dabbler

そしてSELECT * FROM foo WHERE id<4 ORDER BY id DESC LIMIT 1前の記録
2015年

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ステートメントで変数をパラメーターの1つとして取得できませんでした。


あなたのアプローチに興味があります。参加する必要がある場合はどうしますか?たとえば、FROM記事a JOINが作成したb ON ON b.this = a.that。JOINが順序を混乱させているように見えます。ここでは例ですpastebin.com/s7R62GYV
idrarig

1
JOINを行うには、2つの異なる@変数を定義する必要があります。`SELECT current.row、current.id、previous.row、previous.id FROM(SELECT @rownum:= @ rownum + 1 row、a。* FROMアーティクルa、(SELECT @rownum:= 0)r ORDER BY日付、 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 '31 / 10/31

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_rowprevious-> previous_row
Ludovic Guillaume

このクエリは、レコードがテーブルから削除されたかどうか(auto_increment id列のシーケンスが壊れているかどうか)を見つけるのに役立ちます。
Dharmang

@LudovicGuillaumeをもっと具体的にすることができます。申し訳ありませんが、ご理解いただけません。しかし、クエリはprevious_rowで要素がない*。
ウォルター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

私はダンと同じ問題があったので、彼の答えを使用してそれを改善しました。

まず行インデックスを選択します。ここでは何も変わりません。

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つの個別のクエリを使用します。たとえば、行インデックスが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インジェクションに対して脆弱であるため、反対票を投じました。私はこれが古い記事であることを知っていますが、初心者の開発者はそれに気づくべきです。
ayrtonvwf 2018

2

1つのクエリのみを使用するように@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

どうやら、選択列は順番に評価されるので、これはまず保存された変数を選択し、次に変数を新しい行に更新します(プロセスでそれらを選択します)。

注意:これが定義された動作であるかどうかはわかりませんが、使用してみましたが動作します。


1

クエリに複数のフィードid送りnext_id、それらすべてを取得したい場合...

cur_id選択フィールドに割り当て、それをnext_id選択フィールド内に入るサブクエリにフィードします。そしてだけを選択しますnext_id

計算へのロングネック回答の使用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 2015

1

idなどのインデックス列がある場合、1つの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

次を取得してレコードをプレビューする私の解決策は、最後のレコードの場合は最初のレコードに戻り、逆の場合も同様です

私はidを使用していません私は素敵なURLのタイトルを使用しています

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インジェクションに対して脆弱であるため、反対票を投じました。私はこれが古い記事であることを知っていますが、初心者の開発者はそれに気づくべきです。
ayrtonvwf 2018

0

これが私の答えです。「まともなダブラー」からアイデアをピックアップし、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、

最近同じ問題が発生したため、このトピックを見つけました。
デニス

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の場合。
ケビンウォーターソン2017

-2

fooから上位1 *を選択します。id> 4 id ascで順序


1
:等価MySQLは"LIMIT 1"である select * from foo where id>4 order by id asc LIMIT 1
暴徒
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.