pf » LIMIT and OFFSET SQL Pagination
LIMIT and OFFSET SQL Pagination
Both MySQL and PostgreSQL support a really cool feature called OFFSET that is usually used with a LIMIT clause.
The LIMIT clause is used to limit the number of results returned in a SQL statement. So if you have 1000 rows in a table, but only want to return the first 10, you would do something like this:
SELECT column FROM table LIMIT 10
This is similar to the TOP clause on Microsoft SQL Server. However the LIMIT clause always goes at the end of the query on MySQL, and PostgreSQL.
Now suppose you wanted to show results 11-20. With the OFFSET keyword its just as easy, the following query will do:
SELECT column FROM table LIMIT 10 OFFSET 10
This makes it easy to code multi page results or pagination with SQL. Often the approach used is to SELECT all the records, and then filter through them on the application server tier, rather than directly on the database. As you would imagine doing this on the database yields much better performance.
I have known that PostgreSQL supports the OFFSET keyword for quite some time, and for some reason I always thought it was not supported by MySQL. Well it turns out that it is supported now.
Related Entries
- Returning TOP N Records - April 18, 2003
- INFORMATION_SCHEMA Support in MySQL, PostgreSQL - February 18, 2008
- SQL to Select a random row from a database table - September 14, 2005
- SQL Reserved Key Words Checker Tool - March 28, 2005
- Calculating Distance in Miles from Latitude and Longitude - January 18, 2007
SELECT column FROM table LIMIT 10,10
Yes on Oracle you can use ROWNUM to do this, eg: SELECT column FROM table WHERE ROWNUM BETWEEN 10 AND 20
Yes your right OFFSET has been in there for a long time, I think I read once that OFFSET was only supported by PostgreSQL, so I never checked MySQL. Either way, I'm sure lots of folks don't know about it.
Do you know if the LIMIT and OFFSET happen before or after the ORDER clause?
For Oracle: SELECT column FROM table WHERE ROWNUM BETWEEN 10 AND 20 ORDER BY column
I believe that the ORDER clause is applied only to the records that fit the WHERE clause. Not very useful for pagination where ordering is usually required.
Many thanks in advance,
-Bob
I don't use PHP much, so I can't really recommend anything specifically for that.
In general however, you need to check the FOUND_ROWS() as barney mentions to get the total number of results. And also keep a URL variable for the offset.
Then you just need a bit of logic for your next page button, and previous page button. IF the offset variable is at 0 (first page), then don't display previous pages, otherwise subtract limit from offset, and use that in the link for previous.
For next page, display the link if FOUND_ROWS() is greater than offset+limit, the offset for the link to the next page is going to be offset+limit.
Hope that helps you out bob.
-pete
Thanks again,
-Bob
Your url might be something like this /search.cfm?query=chickens&offset=10 that would be the url for page 2. For page three the url might be: /search.cfm?query=chickens&offset=20
So your query will have something like: SELECT stuff from animals WHERE animal LIKE '%#url.query#%' LIMIT 10 OFFSET #url.offset#
Ofcourse you need to make sure that the offset variable is indeed an integer, else you can get hacked with SQL injection hacking.
If you want to truly get pagination in oracle you need to use your query as a subselect:
SELECT * FROM (SELECT column FROM table ORDER BY column) WHERE ROWNUM BETWEEN 10 AND 20
SELECT ... LIMIT 0, 10;
You get the first 10. Try this:
SELECT ... LIMIT 0 OFFSET 10;
You get 0. Don't use it.
SELECT col1 FROM table WHERE rownum BETWEEN 10 AND 20
won't work.
in fact, if you really want to get pagination AND order in a single query on oracle, you have to use the following (this is an example, there are many variants, but it is on of the most simple) :
SELECT col1 FROM (SELECT col1, ROWNUM r FROM (SELECT col1 FROM table ORDER BY col1)) WHERE r BETWEEN
You need three nested selects because : - The rownum is calculated based on the id of each returned row. As long as there are none, ROWNUM stays at 0. Therefor, you need to compute the rownum of each row before sorting some out. - The rownum is computed before the ORDER BY. If you used only two nested selects and did the order and the rownum computing in the same one, it would be as if no sort had been done (from the outer select perspective).
The conclusion of all that is that pagination on oracle can, under certain circumtances, be considers a mistake. In fact, it might, in some cases (in J2EE for instance), prove more efficient than other available solutions).
Un ejemplo para SQL SERVER para emular LIMIT
SQL=" ,(SELECT TOP 1 '<table width=100%><tr><td>'+ISNULL(CIU.nombre,')+'</td><td>'+ISNULL(CIU.apellido,') + '</td><td>'+ ISNULL(CIU.edad,') + '</td><td>' + ISNULL(CASE CIU.sexo WHEN 1 THEN 'Masculino' WHEN 2 THEN 'Femenino' END,')+'</td><td>'+ ISNULL(SUBSTRING(REG.nombre,1,3)+'-'+DEP.nombre+'-'+MUN.nombre,'NACIONAL')+'</td></tr></table>' AS NOMBRE " + " FROM departamento DEP INNER JOIN" + " municipio MUN ON DEP.id_departamento = MUN.id_departamento INNER JOIN" + " regional REG ON DEP.id_regional = REG.id_regional RIGHT OUTER JOIN" + " ciudadano CIU INNER JOIN" + " respuesta RES ON CIU.id_ciudadano = RES.id_ciudadano ON MUN.id_municipio = CIU.id_municipio" + " WHERE (RES.id_pregunta = " + id_pregunta + ") AND (RES.id_cuestionario =CUES.id_cuestionario ) " + " AND CIU.id_ciudadano NOT IN (SELECT TOP 3 CIU.id_ciudadano " + " FROM ciudadano CIU INNER JOIN" + " respuesta RES ON CIU.id_ciudadano = RES.id_ciudadano" + " WHERE RES.id_pregunta = " + id_pregunta + " AND RES.id_cuestionario =CUES.id_cuestionario )) AS CONTACTO_4_"+id_pregunta;
Select TOP 20 * From [Table] WHERE ID > (SELECT TOP 1 x FROM (SELECT TOP 1000 ID as x FROM [Table] ORDER BY ID) as x ORDER BY x DESC)
SELECT * FROM ( SELECT TOP $numRows *, $order AS Ordenar FROM ( SELECT TOP $lastRow * FROM $table WHERE $condition ORDER BY $order ASC) x ORDER BY x.$order DESC) t ORDER BY t.Ordenar
select * from (SELECT fields,rownum r FROM table ) where r between 0 and 20
this will fech 100 records starting from yourrowid.
Rowid is an unique value for each row in oracle.
select * from (select col1, col2..., rownum rn (SELECT distinct col1, col2... FROM table1, table2 WHERE col1=... GROUP... ORDER BY... HAVING...)) --THER GO THE PAGINATION Where rn between rowI and rowJ
select * from (select * from (SELECT distinct col1, col2... FROM table1, table2 WHERE col1=... GROUP... ORDER BY... HAVING...)) --THER GO THE PAGINATION Where rownum between rowI and rowJ
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto05.asp
Instructions for
- SQL Query - Stored Procedure - User-Specific Records - Application-Wide Records
select {your column list} from (select row_number() over (order by {your order by statement}) row_order, {your column list} from {your table}) where row_order between 11 and 20
There are some additional advantages to row_number(). For one you can use partitioning. Say you wanted to pull the top five salaries for each department. If you partition over the department and order by the salary descending you can get numbers 1 through five for each department. (this will mess with your pagination though and you should not use the same row_number() result for both)
That brings me to the second cool thing that you can use row_number() for. You can actually grab multiple ordering results for multiple reasons in one subquery.
Think fantasy football for a minute. You want a listing of players in the top five of passing yards, rushing yards, and catching yards. You can use one query to get them all.
Here is my example:
select player_name, passing_yards, rushing_yards, catching_yards from (select row_number() over (order by passing_yards desc) passing_rank, row_number() over (order by rushing_yards desc) rushing_rank, row_number() over (order by catching_yards desc) catching_rank, player_name, passing_yards, rushing_yards, catching_yards from player_stats) where passing_rank < 6 or rushing_rank < 6 or catching_rank < 6 order by player_name;
using partitioning you can do the same thing in one query for the top five for each stat in AFC vs NFC.
select conference, player_name, passing_yards, rushing_yards, catching_yards from (select row_number() over (partition by conference order by passing_yards desc) passing_rank, row_number() over (partition by conference order by rushing_yards desc) rushing_rank, row_number() over (partition by conference order by catching_yards desc) catching_rank, conference, player_name, passing_yards, rushing_yards, catching_yards from player_stats) where passing_rank < 6 or rushing_rank < 6 or catching_rank < 6 order by conference, player_name;
That's some good stuff! Chris S.
SELECT * FROM( SELECT a.*, ROWNUM r FROM ( SELECT column FROM table[,....] [WHERE clause] ORDER BY column[,...]) a ) WHERE r BETWEEN start AND end
and i have putted it into scott schema
SELECT * FROM ( SELECT a.*, ROWNUM r FROM EMP a ORDER BY a.ENAME ) WHERE r BETWEEN 2 AND 4 ORDER BY r
it gives me result like follwoing ================================= EMPNO ENAME JOB R 7499 ALLEN SALESMAN 2 7521 WARD SALESMAN 3 7566 JONES MANAGER 4
===================================
but i want result like below
EMPNO ENAME JOB R 7499 ALLEN SALESMAN 2 7698 BLAKE MANAGER 6 7782 CLARK MANAGER 7
=================================
i got this only from my below query
SELECT * FROM ( SELECT b.*, ROWNUM r1 FROM ( SELECT a.empno, a.ename, a.job, ROWNUM r FROM EMP a ORDER BY a.ENAME ) b ) WHERE r1 BETWEEN 2 AND 4 ORDER BY r
==================================
may be there are any othe better solution then this but i haven't find your solution workable according to the requirements.
i have used the following query ==============================
SELECT TO_NUMBER(SUBSTR(A,1,4)) - 2000 years, TO_NUMBER(SUBSTR(A,6,2)) - 01 months, TO_NUMBER(SUBSTR(A,9,2)) - 01 days, SUBSTR(A,12,2) hours, SUBSTR(A,15,2) minutes, SUBSTR(A,18,2) seconds FROM (SELECT TO_CHAR(TO_DATE('20000101','YYYYMMDD') + ( SELECT SUM(TRUNC((((86400*(JOB_HISTORY.END_DATE - JOB_HISTORY.FROM_DATE))/60)/60)/24) ) FROM JOB_HISTORY WHERE employee_id='0564'
),'YYYY MM DD HH24:MI:SS') A FROM DUAL)
but it also add up the experience of also those jobs which have been done simultaneously. for example
for this particular employee with following job history =================
DAYS END_DATE FROM_DATE 819 30-Apr-06 01-Feb-04 668 30-Apr-06 01-Jul-04
experience should be shown near about 2 years 2 months AND 29 days but my query shows it like below
YEARS MONTHS DAYS 4 0 26
which is incorrect but for above the following query gives accurate result
SELECT
TRUNC( MONTHS_BETWEEN( MAX(END_DATE),MIN(FROM_DATE) ) /12 ) Years, MOD( TRUNC( MONTHS_BETWEEN( MAX(END_DATE),MIN(FROM_DATE) ) ), 12 ) months, MAX(END_DATE) - ADD_MONTHS(MIN(FROM_DATE),TRUNC( MONTHS_BETWEEN( MAX(END_DATE),MIN(FROM_DATE) ) )) days FROM JOB_HISTORY WHERE employee_id='1380' ORDER BY end_date DESC
but it fails when an employee having experience with some pauses
==============
DAYS END_DATE FROM_DATE 731 01-Feb-93 01-Feb-91 586 09-Dec-99 02-May-98
for this particular case my first query runs smoothly.
i will really appreciate if any body answer my this question as soon as possible withing one query.
Thanx in advance
Do a subselect that only queries the primary table; apply the pagination logic on that subquery; and then join your secondary table to the result of the subquery.
http://www.score5.org/articles/show/3
it covers how to do joined queries
so..can anybody tell me...whats the finally solution for pagination with ORACLE... I HAVE A PROGRAM IN JAVA AND I CONSULT WITH A QUERY, SOME REGISTER FROM ORACLE BD. THEN I PRINT THIS INFORMATION IN A JTABLE AND WELL I HAVE A PROBLEM HERE BECAUSE IT'S TO MUCH INFORMATION AROUND 2.500.000 REGISTERS.. SO I NEED HELP WITH THE PAGINATION THING.
http://www.singingeels.com/Articles/Pagination_In_SQL_Server_2005.aspx
I'll tinker around with it and see if it works.
- CFSCRIPT Cheatsheet
- 3 New Image Effects for ColdFusion 8
- Googlebot to Submit Web Forms
- ColdFusion 8 Update 1 Fixes some Image Processing Quirks
- 10 Most Useful Image Functions in ColdFusion 8
- Speaking at NYC CFUG This Week
- Adobe AIR Tutorial for HTML / JavaScript Developers
- INFORMATION_SCHEMA Support in MySQL, PostgreSQL
RSS
add to del.icio.us
Pete Freitag is a software engineer, and web developer located in










