SOQL Offset in Spring ’12

Spring ’12 is upon us and it comes bearing plenty of gifts for the developer. One of more important features in Spring ’12 is the addition of the “OFFSET” clause to SOQL. With this clause, you can now paginate through your result set directly in SOQL. Prior to this feature, there were a couple of ways to implement pagination in When using one of the APIs (SOAP or REST), you already had pagination built in via the query/queryMore calls. In the context of pagination in a Visualforce page, the ideal implementation was to use a StandardSetController. However many developers also chose to implement their own custom pagination logic using custom SOQL queries and/or with some client side JavaScript. The new OFFSET clause should make any custom pagination logic on the platform really simple. Lets take a closer look at this new clause.

As an example, say you wanted to develop a Visualforce page for paginating over a set of Account records. As stated earlier, the ideal implementation for this use case is still to use a StandardSetController, but for the purposes of illustration lets see how you could implement this using the new OFFSET clause. You can find the entire code for this sample on GitHub, but lets focus on the Apex extension class for now.

public with sharing class CustomPaginationExt {

    public List accounts{get;set;}
    public Integer pageSize = 10;
    public Integer pageNumber = 0;
    private String baseQuery = 'SELECT name, industry FROM Account ORDER BY name';

    public PageReference next(){
        return null;

    public PageReference previous(){
        if (pageNumber < 0)
            return null;
        return null;

    private void queryAccounts()
        Integer offset = pageNumber * pageSize;
        String query = baseQuery + ' LIMIT '+pageSize +' OFFSET '+ offset;
            accounts = Database.query(query);
        catch(Exception e){

Each time that the user clicks on the “Next’ or ‘Previous’ link on the page, the pageNumber variable is updated and the queryAccounts method is called. This is where the OFFSET magic happens. The OFFSET clause specifies the starting row offset into the result set returned by your query. So for example if your query returned a total of 30 Account records, adding a ‘ LIMIT 10 OFFSET 20’ clause would return rows 21-30. This is the exact logic implemented in the queryAccounts method.

A couple of additional notes on the use of the OFFSET clause

  • This feature is currently in Developer Preview and you’ll need to contact Salesforce support to enable it in your Org.
  • It is usually a best practice to use a ORDER BY clause when you use OFFSET  (as shown above) to ensure that the result set ordering is consistent.
  • Similarly, it is recommended that you use a LIMIT clause in combination with OFFSET if you need to retrieve subsequent subsets of the same result set. That’s how for example the queryAccounts method can keep getting the next/previous set of 10 Account records from the initial result set.
  • OFFSET is applied to the result set returned at the time of the query. The paging results may change if the underlying data is modified (for example if someone inserts/deletes records) between multiple queries using OFFSET into the same result set. If this is of concern to your design, you’ll need to use the query/queryMore pattern of the API instead.
As always, questions and comments are welcome. Till then, enjoy your time with Spring ’12.
tagged , , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • does this support campaign member as well?

    • Anonymous

      Yes. SOQL OFFSET works on all standard and custom objects and so should work with the CampaignMember sobject as well.

  • Can you use this within batch apex?

    • Anonymous

      Yes – you can use SOQL Offset in Batch Apex.

  • Mudit

    Could you please write vf page which use the above class for pagination.?


  • sush

    how can i disable next when there is the last page or disable previous when i am on the first page

    this is my controller:
    public with sharing class OppOPLiOffSetPagination {

    public Opportunity opp { get;set; }
    public Integer pageNumberOPLI = 0 ;
    public List listOfOpli { get; set; }
    private Integer pageSizeOPLI = 5;

    public OppOPLiOffSetPagination() {
    this.opp = [ Select Id, Name, Account.Name, Amount
    from Opportunity
    where Id = :ApexPages.currentPage().getParameters().get(‘id’)];
    this.listOfOpli = new List();


    public PageReference opprtunityLIneitemList(){
    return null;

    private void queryOPLI(){
    Integer OffsetSize = pageNumberOPLI *pageSizeOPLI;
    this.listOfOpli = [ Select Product2.Id, TotalPrice, Quantity, Product2.ProductCode, UnitPrice,
    Product2.Family, Product2.Name , OpportunityId from OpportunityLineItem
    where OpportunityId =:this.opp.Id order by Name limit :pageSizeOPLI
    offset :OffsetSize ];


    public PageReference Next() {
    return null;

    public PageReference Previous() {
    if (pageNumberOPLI < 0)
    return null;
    return null;


    and this is my page: