SQL提取当前行的上一行或下一行

12
id    |  照片标题        |  创建日期
XEi43 | 我的家庭 | 2009年08月04日 dDls | 朋友团体 | 2009年08月05日 32kJ | 美丽的地方 | 2009年08月06日 EOIk | 工作到深夜 | 2009年08月07日

假设我有一个id为32kJ,如何获取上一行或下一行?


“下一个”或“上一个”的概念取决于顺序。 您可能会通过使用ado.net或其他技术检索DataSet,然后循环遍历该数据表示的记录来实现此目的。 - pvieira
1
如果您的 ID 字段是连续和数字的,那么这很容易做到,但事实上,您使用的字母字符使得它变得更加棘手。http://www.scottklarr.com/topic/111/how-to-select-previousnext-rows-in-mysql/ - Mr. Smith
6个回答

17

这是我用来查找前/后记录的方法。 表中的任意列都可以用作排序列,而不需要连接或使用复杂的技巧:

下一条记录(日期大于当前记录):

SELECT id, title, MIN(created) AS created_date
FROM photo
WHERE created >
  (SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created ASC
LIMIT 1;

之前的记录(日期小于当前记录):

SELECT id, title, MAX(created) AS created_date
FROM photo
WHERE created <
  (SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created DESC
LIMIT 1;

示例:

CREATE TABLE `photo` (
    `id` VARCHAR(5) NOT NULL,
    `title` VARCHAR(255) NOT NULL,
    `created` DATETIME NOT NULL,
    INDEX `created` (`created` ASC),
    PRIMARY KEY (`id`)
)
ENGINE = InnoDB;

INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('XEi43', 'my family',       '2009-08-04');
INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('dDls',  'friends group',   '2009-08-05');
INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('32kJ',  'beautiful place', '2009-08-06');
INSERT INTO `photo` (`id`, `title`, `created`) VALUES ('EOIk',  'working late',    '2009-08-07');

SELECT * FROM photo ORDER BY created;
+-------+-----------------+---------------------+
| id    | title           | created             |
+-------+-----------------+---------------------+
| XEi43 | my family       | 2009-08-04 00:00:00 |
| dDls  | friends group   | 2009-08-05 00:00:00 |
| 32kJ  | beautiful place | 2009-08-06 00:00:00 |
| EOIk  | working late    | 2009-08-07 00:00:00 |
+-------+-----------------+---------------------+


SELECT id, title, MIN(created) AS next_date
FROM photo
WHERE created >
  (SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created ASC
LIMIT 1;

+------+--------------+---------------------+
| id   | title        | next_date           |
+------+--------------+---------------------+
| EOIk | working late | 2009-08-07 00:00:00 |
+------+--------------+---------------------+

SELECT id, title, MAX(created) AS prev_date
FROM photo
WHERE created <
  (SELECT created FROM photo WHERE id = '32kJ')
GROUP BY created
ORDER BY created DESC
LIMIT 1;

+------+---------------+---------------------+
| id   | title         | prev_date           |
+------+---------------+---------------------+
| dDls | friends group | 2009-08-05 00:00:00 |
+------+---------------+---------------------+

我还没有尝试过,但我相信这将会得出结果,我只是想知道哪一个查询更快。 - Basit
1
如果你已经将结果排序并将其限制为1行,为什么要使用聚合函数呢? - PhoneixS

2
我知道您正在使用MySQL,但只是为了参考,以下是使用Oracle的分析函数LEAD和LAG执行此操作的方法:
select empno, ename, job,
  lag(ename, 1) over (order by ename) as the_guy_above_me,
  lead(ename, 2) over (order by ename) as the_guy_two_rows_below_me
from emp
order by ename

我想Oracle收费,而MySQL免费,这其中肯定有原因... :-)
这个页面向您展示如何在MySQL中模拟分析函数

2
在Postgres 8.4中,您也可以获得这些分析函数 :p - araqnid

2

您是否想按日期获取下一行/上一行数据?如果是这样,您可以这样做:

select MyTable.*
from MyTable
join
  (select id
   from MyTable
   where created_date < (select created_date from MyTable where id = '32kJ')
   order by created_date desc, id desc
   limit 1
  ) LimitedTable on LimitedTable.id = MyTable.fund_id;

Jeremy Stein,能否告诉我如何检查当前行在总行数中的位置(例如显示6/100行),这个查询可以吗?还是需要扩展查询?如果需要扩展,应该如何操作?我只想知道当前行在总行中的位置,以便使其与上一条和下一条一起合作。这样用户就可以知道他们所在的位置,就像在Facebook上那样。 - Basit
我想为当前行添加位置,而不是下一行和上一行.. 我该怎么做? http://stackoverflow.com/questions/2036425/little-complex-sql-row-postion - Basit
是的,你可以做那些事情,但从调用应用程序中执行可能更有意义。 - Jeremy Stein

1

使用麦克的MAX/MIN技巧,我们可以为各种事情制作上一个/下一个跳转。这个msAccess示例将返回股票市场数据表中每个记录的前一次收盘价。注意:'<='是为周末和节假日准备的。

SELECT 
   tableName.Date,
   tableName.Close,
   (SELECT Close 
      FROM tableName 
      WHERE Date = (SELECT MAX(Date) FROM tableName 
                     WHERE Date <= iJoined.yesterday)
   ) AS previousClose
FROM 
 (SELECT Date, DateAdd("d",-1, Date) AS yesterday FROM tableName)
  AS iJoined 
INNER JOIN 
    tableName ON tableName.Date=iJoined.Date;

...'yesterday'演示了如何使用函数(Date-1)进行跳转;我们也可以简单地使用...

(SELECT Date FROM tableName) AS iJoined
  /* previous record */
(SELECT MAX(Date) FROM tableName WHERE Date < iJoined.Date)
  /* next record */
(SELECT MIN(Date) FROM tableName WHERE Date > iJoined.Date)

诀窍是我们可以使用 MAX\MIN 和 jump function() 来处理前面和后面 # 个某物


0
可怕的黑客 - 我不喜欢这个,但可能会起作用..
with yourresult as
(
select id, photo_title, created_date, ROW_NUMBER() over(order by created_date) as 'RowNum' from your_table
)
-- Previous
select * from yourresult where RowNum = ((select RowNum from yourresult where id = '32kJ') -1)
-- Next
select * from yourresult where RowNum = ((select RowNum from yourresult where id = '32kJ') +1)

有用吗?


很遗憾,可能不行。这是MS SQL - 昨天午餐时我匆忙思考了一下,但时间不多 - 我今晚会再看一下。 - Tikeb
我遇到了错误 #1064 - 您的SQL语法有误;请检查与您的MySQL服务器版本相对应的手册,以获取正确的语法使用方式,位于第1行附近,具体为: with yourresult as ( select photo_id, title, added_date, ROW_NUMBER() over(ord - Basit

0
我将id视为表中的主键(也是“行号”),并使用它将每个记录与前一个记录进行比较。 以下代码应该可以正常工作。
CREATE SCHEMA temp
create table temp.emp (id integer,name varchar(50), salary  varchar(50));
insert into temp.emp values(1,'a','25000');
insert into temp.emp values(2,'b','30000');
insert into temp.emp values(3,'c','35000');
insert into temp.emp values(4,'d','40000');
insert into temp.emp values(5,'e','45000');
insert into temp.emp values(6,'f','20000');

select * from temp.emp

SELECT
    current.id, current.name, current.salary,
    case 
        when current.id = 1 then current.salary 
        else 
            case
                when current.salary > previous.salary then previous.salary
                else current.salary  
            end
     end
FROM
    temp.emp AS current
    LEFT OUTER JOIN temp.emp AS previous
    ON current.id = previous.id + 1

你需要在单个查询中比较前一条记录和后一条记录的解决方案吗?那就试试上面的方法。 - Rahul More

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接