12
id    |  photo title     |  created_date

XEi43 |  my family       |  2009 08 04
dDls  |  friends group   |  2009 08 05
32kJ  |  beautiful place |  2009 08 06
EOIk  |  working late    |  2009 08 07 

Say I have the id 32kJ. How would I get the next row or the previous one?

alex
  • 10,900
  • 15
  • 70
  • 100
Basit
  • 16,316
  • 31
  • 93
  • 154
  • The concept of "next" or "previous" depends on the order. You probably might do this programatically, by retriving a DataSet using ado.net or other technology, and then looping through the records of that representation of the data. – pvieira Aug 11 '09 at 10:15
  • 1
    It is easily doable if your id field was sequential and numeric, the fact that you're using alphabetical characters makes it a lot trickier. http://www.scottklarr.com/topic/111/how-to-select-previousnext-rows-in-mysql/ – Mr. Smith Aug 11 '09 at 10:17

6 Answers6

17

This is what I use for finding previous/next records. Any column in your table can be used as the sort column, and no joins or nasty hacks are required:

Next record (date greater than current record):

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;

Previous record (date less than current record):

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;

Example:

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 |
+------+---------------+---------------------+
Mike
  • 21,301
  • 2
  • 42
  • 65
  • i havent tried it, but im sure this will pull the result, i just wonder, which one if more fast query. – Basit May 14 '10 at 19:04
  • 1
    Why do you use aggregated functions if you already have ordered the results and limited it to 1 row? – PhoneixS Aug 11 '15 at 15:04
2

I realize that you are using MySQL, but just for reference, here is how you would do this using Oracle's analytic functions LEAD and 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

I guess there's a reason why Oracle costs money and MySQL is free... :-)

This page shows you how to emulate analytic functions in MySQL.

ObiWanKenobi
  • 14,526
  • 11
  • 45
  • 51
2

Did you want the next/previous row by date? If so, you could do this:

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
  • 19,171
  • 16
  • 68
  • 83
  • jeremy stein, can you also please tell me, if i can check postion of current row from total rows (showing 6 of 100 rows), can this query do it or would i need to extend the query, if extend, then how or what do i have to do? i just want to also know the position of current row from total rows, so i can make it work with next and previous.. so users know where they at, like on facebook. – Basit Jan 10 '10 at 08:04
  • i want to add positioning too for current row, other then next and previous.. how can i do that? http://stackoverflow.com/questions/2036425/little-complex-sql-row-postion – Basit Jan 10 '10 at 08:15
  • Yes, you could do those things, but it probably would make more sense to do it from the calling application. – Jeremy Stein Jan 11 '10 at 16:11
1

Using Mike's MAX/MIN trick we can make previous\next jumps for all sorts of things. This msAccess example will return the previous close for every record in a stock market data table. Note: the '<=' is for weekends and holidays.

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' demonstrates using a function(Date-1) jump; we could have simply used...

(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)

The trick is we can previous\next # of whatever(s) with MAX\MIN and a jump function()

0

Horrible hack - I don't like this but might work..

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)

That of any use?

Tikeb
  • 978
  • 3
  • 9
  • 25
  • probably not, unfortunatly. It's MS SQL - I had a quik think yesterday lunch but didn't have much time - I'll have another look tonight.. – Tikeb Aug 12 '09 at 12:29
  • im getting this error #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'with yourresult as ( select photo_id, title, added_date, ROW_NUMBER() over(ord' at line 1 – Basit Aug 12 '09 at 17:18
0

I considered id as primary key in the table (and as "row number"), and used it to compare each record with the record before. The following code must work.

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
fableal
  • 1,577
  • 10
  • 24