Implementing Pagination in MySQL Queries with Go
Last Updated :
21 Oct, 2024
Pagination is an important method in web applications and APIs in the task of working with extensive data. Pagination reveals data in reasonable portions instead of a complete set because the later consumes time, slows website functionality, and may present numerous records to the user. This makes the interface easier for the user to use and explore by breaking data into smaller sections of data which can be accessed using buttons such as; back and forward buttons.
In backend systems, pagination is useful to make the server’s job easier and use lesser memory to transfer data over the network. Instead of retrieving all the data, only the data fields that are needed are retrieved, thus these systems can remain efficient and potentially scalable.
Understanding SQL LIMIT and OFFSET
There are a set of MYSQL commands for retrieving a subset of rows from a table including the LIMIT and OFFSET commands. As for the LIMIT clause, it points to the number of rows that can be selected, whereas by the OFFSET clause, the number of rows that may be skipped points to.
Example
SELECT * FROM users LIMIT 10 OFFSET 20;
In this query:
- LIMIT 10 instructs MySQL to return 10 rows.
- OFFSET 20 skips the first 20 rows, returning rows 21 to 30.
For example, if you want to display 10 items per page:
- Page 1: LIMIT 10 OFFSET 0 (returns rows 1–10)
- Page 2: LIMIT 10 OFFSET 10 (returns rows 11–20)
- Page 3: LIMIT 10 OFFSET 20 (returns rows 21–30)
Writing a Paginated MySQL Query in Go
Let’s see how to implement a paginated query using Go.
Go
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func getPaginatedUsers(db *sql.DB, page int, limit int) {
// Calculate the offset
offset := (page - 1) * limit
// Query with LIMIT and OFFSET
query := fmt.Sprintf("SELECT id, name, email FROM users LIMIT %d OFFSET %d", limit, offset)
rows, err := db.Query(query)
if err != nil {
panic(err)
}
defer rows.Close()
// Loop through the result set
for rows.Next() {
var id int
var name, email string
if err := rows.Scan(&id, &name, &email); err != nil {
panic(err)
}
fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
}
}
func main() {
// Open database connection
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/testdb")
if err != nil {
panic(err)
}
defer db.Close()
// Fetch paginated results (e.g., Page 2, 10 items per page)
getPaginatedUsers(db, 2, 10)
}
In this example:
- The getPaginatedUsers function calculates the offset based on the requested page and limit.
- The SQL query uses LIMIT and OFFSET to fetch the desired subset of users from the database.
Handling Pagination in the Go Application
To paginate data based on user input, you'll typically retrieve the page and limit values from HTTP request parameters. Here's an example using Go's built-in HTTP package:
Go
package main
import (
"fmt"
"net/http"
"strconv"
)
func paginateHandler(w http.ResponseWriter, r *http.Request) {
// Get 'page' and 'limit' from query parameters
page, err := strconv.Atoi(r.URL.Query().Get("page"))
if err != nil || page < 1 {
page = 1 // default to page 1
}
limit, err := strconv.Atoi(r.URL.Query().Get("limit"))
if err != nil || limit < 1 {
limit = 10 // default to 10 items per page
}
// Pass page and limit to the pagination function (database interaction code not shown)
fmt.Fprintf(w, "Page: %d, Limit: %d", page, limit)
}
func main() {
http.HandleFunc("/users", paginateHandler)
http.ListenAndServe(":8080", nil)
}
Here, page and limit are fetched from the query parameters (/users?page=2&limit=10). Default values are provided if the parameters are missing or invalid.
Managing Edge Cases in Pagination
When implementing pagination, it's important to handle edge cases, such as:
- No More Records: If a user makes a query for a page greater than the specified number of records then set the resulting table to an empty value.
- Invalid Page Numbers: If the page no is a negative or zero then set page no to 1.
- Setting a Maximum Limit: Providing finite records per page is again a great measure to prevent the system from being overwhelmed. For instance, restrict the number of records per page to be not more than one hundred.
Example: Handling Edge Cases
Go
func getPaginatedUsers(db *sql.DB, page, limit int) {
if limit > 100 {
limit = 100 // set a maximum limit
}
offset := (page - 1) * limit
query := fmt.Sprintf("SELECT id, name, email FROM users LIMIT %d OFFSET %d", limit, offset)
rows, err := db.Query(query)
if err != nil {
panic(err)
}
defer rows.Close()
if !rows.Next() {
fmt.Println("No records found.")
return
}
// process rows...
}
Returning Paginated Results to the User
When returning paginated results, it’s important to include metadata such as:
- Total records: It’s the total number of rows available.
- Current page: This is the number of the page the user is on when using the system.
- Total pages: The maximum number of pages the site can provide.
- Items per page: It shows the number of items that a page will be able to contain.
Example: Structuring the Response
Go
type PaginatedResponse struct {
Data []User `json:"data"`
Total int `json:"total"`
Page int `json:"page"`
TotalPages int `json:"total_pages"`
Limit int `json:"limit"`
}
func returnPaginatedUsers(w http.ResponseWriter, users []User, total, page, limit int) {
totalPages := (total + limit - 1) / limit // calculate total pages
response := PaginatedResponse{
Data: users,
Total: total,
Page: page,
TotalPages: totalPages,
Limit: limit,
}
json.NewEncoder(w).Encode(response)
}
Optimizing Pagination for Performance
When working with big datasets, pagination can be a real performance issue, if it is not correctly implemented. Here are a few tips:
- Use Indexed Columns: As a best practice, always paginate on an indexed column where possible so that you don’t complete Queries will take longer to execute.
- Avoid Large OFFSETs: Large offsets can be very detrimental to query performance because MySQL effectively has to do page surfing on each query. However, one should better use cursor-based pagination or the last retrieved ID can be stored and the records with ID greater than the last ID can be fetched.
Cursor-based Pagination Example:
SELECT * FROM users WHERE id > ? LIMIT 10;
Conclusion
Veteran in computer science knows that pagination is very important for both scalability and usability in an application that deals with large data sets. But here, by using LIMIT and OFFSET that is provided by the SQL language along with the Go’s efficient structure, you can easily include the concept of pagination. Pagination is all about managing the display and streaming of large data sets, and to be on the safe side, you need to take your time and address specific considerations when making your pagination script.
Similar Reads
Servlet - Pagination with Example
Pagination is a sequence of pages that are connected and have similar content. Pagination is a technique for splitting a list of multiple records into subsists. For example, you search with a keyword on Google and receive tens of thousands of results. It is important to note that even when the conte
5 min read
Spring MVC - Pagination with Example
We will be explaining how we can implement pagination in Spring MVC Application. This is required when we need to show a lot of data on pages. Suppose in an e-commerce site we have a lot of products but we can't show all of those on a single page, so we will show only 20 products on each page. This
3 min read
How to do pagination Node.js with MySQL ?
Node.js is a runtime environment like Chrome's V8 JavaScript engine. Node.js is an open-source, cross-platform, and backend runtime environment that executes outside a web browser. MySQL is an open-source relational database management system that is fast, reliable, flexible, and robust. Both MySQL
9 min read
Pagination Using MySQL LIMIT, OFFSET
Pagination enables users to view a limited set of records per page. It is very useful in database-driven applications as it makes it easier for users to view and understand information. MySQL provides a powerful mechanism for achieving pagination using the LIMIT and OFFSET clauses. This guide will e
5 min read
What are Different Methods to Do Pagination in PostgreSQL?
In the area of database management, PostgreSQL has a diversity of methods to optimally implement pagination traversing over a range of data retrieving needs. From traditional set-based pagination to very advanced keyset pagination and window functions, PostgreSQL has a wide range of techniques that
4 min read
Performing complex queries in MongoDB with Node.js
MongoDB is a popular NoSQL database known for its flexibility and scalability. When working with the MongoDB in the Node.js application. We often need to perform complex queries to retrieve or manipulate the data effectively. This article will guide you through the various approaches to performing c
5 min read
How to Use Go with MySQL?
MySQL is an open-source relational database management system based on Structured Query Language(SQL). It is a relational database that organizes data into one or more tables in which data are related to each other. Database Driver: A Database Driver implements a protocol for a database connection.
4 min read
Executing Different MySQL Queries Using Go
Go, often referred to as Golang, is a programming language that has gained immense popularity due to its simplicity, speed, and strong support for concurrent programming. It was developed by Google to tackle large-scale system programming challenges, and one of its key advantages is its ability to e
5 min read
io.Pipe() Function in Golang with Examples
In Go language, io packages supply fundamental interfaces to the I/O primitives. And its principal job is to enclose the ongoing implementations of such king of primitives. The Pipe() function in Go language is used to create a concurrent in-memory pipe and can be applied in order to link the code t
3 min read
strings.LastIndexAny() Function in Golang With Examples
strings.LastIndexAny() Function in the Golang is used to find the index of the last instance of any Unicode code point from chars in a given string. If the Unicode code point from chars is not found then it returns -1. Thus, this function returns an integer value. The index is counted taking zero as
2 min read