After you execute a JSTL query, the result set is assigned to the scoped var variable as an instance of the javax.servlet.jsp.jstl.sql.Result interface. This object provides properties for accessing the rows, column names, and size of the query’s result set:

Property Description
rows An array of SortedMap objects, each of which maps column names to a single row in the result set
rowsByIndex An array of arrays, each corresponding to a single row in the result set
columnNames An array of strings naming the columns in the result set, in the same order as used for the rowsByIndex property
rowCount The total number of rows in the query result
limitedByMaxRows True if the query was limited by the value of the maxRows attribute

Some usage examples as requested in the first comment to the blog:

First of all, you have to set up a data source, e.g. to connect to an Oracle database:

    <sql:setDataSource var="dataSrc"
       url="jdbc:oracle:thin:@127.0.0.1:1521:database_name"
       driver="oracle.jdbc.driver.OracleDriver"
       user="user_name" password="pass_word"/>

Then you run a query:

   <sql:query var="queryResults" dataSource="${dataSrc}">

        select system_id, employeename from employees

   </sql:query>
 Then, you display the results on the web page:

<table>
     <tr>
       <th>ID</th>
       <th>Name</th>
     </tr>
   <c:forEach var="row" items="${queryResults.rows}">
     <tr>    
       <td>   
          <c:out value="${row.system_id}"/>
       </td>
       <td><c:out value="${row.employeename}"/></td>
     </tr>
   </c:forEach>    
   </table>
 

This is as simple an example as you can get. The bottom line is that when you use the rows property, you refer to values by column names.

Here is an example that uses rowsByIndex property:

<c:forEach var="row" items="${queryResults.rowsByIndex}">
   <tr>    
       <td>   
          <c:out value="${row[0]}"/>
       </td>
       <td><c:out value="${row[1]}"/></td>
     </tr>
</c:forEach>     
 
Same resultset, you just refer to column values by the column index property.
   
The next example shows how to use the columnNames property:
      
<table>
     <tr>
       <th><c:out value="${queryResults.columnNames[0]}"/></th>
       <th><c:out value="${queryResults.columnNames[1]}"/></th>
     </tr>
    …
   </table>
    
 In this case, the table headings will be  system_id and employeename.
 
 The rowCount property is self-evident. It shows the number of rows returned by your query:

          <c:out value="${queryResults.rowCount}"/>

  
 You use the limitedByMaxRows property if you used the maxRows attribute in your query:
 
<sql:query var="queryResults" dataSource="${dataSrc}"
                    maxRows="20">
In this case, the resultset will contain only 20 rows.              

 <c:if test="${queryResults.limitedByMaxRows}">
                Your query returned too many rows.
</c:if>

   The limitedByMaxRows propery can be used for pagination purposes.
  

Now, I will show how to get to display a resultset from any sql statement. The resulting table will display field names as column headings. This code is pretty generic.

    <table>      
     <tr>
         <c:forEach var="columnName" items="${queryResults.columnNames}">
            <th><c:out value="${columnName}"/></th>
          </c:forEach>
      </tr>
    <c:forEach var="row" items="${queryResults.rows}">
          <tr>
              <c:forEach var="columnName" items="${queryResults.columnNames}">
                <td><c:out value="${row[columnName]}"/></td>
             </c:forEach>           
         </tr>
     </c:forEach>     
    </table>      

Visit this page to learn how to display and sort request parameters with  JSTL.