PHP Search and Pagination Debugger for MySQL Projects

Free PHP/MySQL Search Debugging Tool

PHP Search, Filter, Pagination and SQL LIMIT/OFFSET Debugger

Find whether your PHP search or pagination problem is caused by the keyword pattern, prepared statement, count query, LIMIT, OFFSET, page calculation, missing filters, unsafe sorting, JOIN duplicates or lost query parameters.

Use this tool when: search returns no records, page 2 repeats page 1, rows are skipped, the last page is empty, filters disappear after navigation, total pages are incorrect, JOINs create duplicate records or an AJAX “Load More” button repeatedly loads the same results.

SQL LIKE LIMIT OFFSET PDO MySQLi Filter URLs Safe sorting AJAX load more

Describe Your Search or Pagination Problem

Complete the settings you know. The optional SQL and PHP code fields improve the diagnosis but are not required.

Local browser analysis: this tool does not connect to your database or execute pasted SQL. Remove passwords, private customer data, session IDs and production credentials before pasting code.

1. Environment and feature

2. Request and URL parameters

3. Record counts and pagination calculation

4. Search, query and sorting behavior

5. Optional sanitized SQL and PHP code

Personalized Pagination Diagnosis

Likely failing layer

Diagnostic confidence 0%
Confirm the result using the final SQL, bound values, filtered count, displayed IDs and generated pagination URLs.
Normalized page 1
Correct OFFSET 0
Total pages 1
Expected rows this page 0

Most Likely Root Causes

    Fix in This Order

      Search and SQL Checks

        Pagination and URL Checks

          Stage-by-Stage Test Matrix

          Stage Assessment Exact test

          Personalized PHP and SQL Templates

          These templates use sanitized example identifiers. Match the real connection variable, table names, filter rules, authorization conditions and database indexes before placing them in a project.

          PDO search and pagination template
          MySQLi search and pagination template
          Filter-preserving pagination links
          Safe ORDER BY allowlist
          AJAX Load More pattern

          Security and Data-Safety Warnings

            Search and Pagination Test Cases

              Viva-Ready Explanation

              Copyable Debugging Report

              How PHP Search and Pagination Work Together

              Search and pagination are two parts of the same database operation. The search decides which records belong in the result set. Pagination decides which small section of that filtered result set should appear on the current page.

              A reliable implementation normally runs two related queries. The count query calculates how many filtered records exist. The data query uses the same filters, adds a stable order and retrieves one page through LIMIT and OFFSET.

              Input stage

              PHP reads and validates the keyword, filters, page number, per-page value and selected sort option.

              Filter stage

              PHP builds one controlled WHERE condition and prepares the corresponding parameter values.

              Count stage

              A filtered count determines the number of available records and pages.

              Offset stage

              The current page and page size produce the zero-based SQL offset.

              Data stage

              The data query uses matching filters, a stable order, LIMIT and OFFSET.

              Navigation stage

              Pagination links preserve the keyword, filters and sorting choice.

              The Correct LIMIT and OFFSET Calculation

              User-facing page numbers usually begin at one, while SQL offsets begin at zero. Therefore, page one begins at offset zero. Page two begins after one complete page of records.

              $offset = ($currentPage - 1) * $recordsPerPage;
              Page Records per page OFFSET Returned positions
              1 10 0 Rows 1–10
              2 10 10 Rows 11–20
              3 10 20 Rows 21–30

              Using $currentPage * $recordsPerPage skips the first group because page one starts at offset ten. Failing to apply the calculated offset causes every page to return the same first records.

              Validate the Page Number Before SQL

              A page number comes from the browser and must not be trusted. It may be missing, zero, negative, decimal text or extremely large.

              $currentPage = filter_input(
                  INPUT_GET,
                  "page",
                  FILTER_VALIDATE_INT
              );
              
              $currentPage = max(1, $currentPage ?: 1);

              After the count query calculates the total pages, clamp the requested value again. A request for page 500 should not silently produce a confusing empty page when only five pages exist.

              $totalPages = max(
                  1,
                  (int) ceil($totalRecords / $recordsPerPage)
              );
              
              $currentPage = min($currentPage, $totalPages);

              Why Page 2 Repeats Page 1

              Repeated pages usually mean the offset was calculated but never added to the executed SQL, the offset variable remains zero, or the page parameter is read from the wrong request key.

              Inspect these values together:

              var_dump([
                  "page" => $currentPage,
                  "per_page" => $recordsPerPage,
                  "offset" => $offset
              ]);

              Also inspect the IDs returned on each page. Page one and page two should not contain the same complete ID sequence when the order and dataset remain unchanged.

              Why Search and Filters Disappear on Page 2

              A page link such as ?page=2 replaces the existing query string. If the active keyword, status, category or sort option is not included, PHP receives only the page number and rebuilds an unfiltered query.

              Build links from all active parameters:

              $query = http_build_query([
                  "search" => $search,
                  "status" => $status,
                  "sort" => $sort,
                  "page" => $pageNumber
              ]);
              
              $url = "records.php?" . $query;

              This also handles spaces and special URL characters more safely than manually joining raw values with ampersands.

              The Count Query Must Match the Data Query

              The total-page calculation is only correct when the count query represents the same filtered dataset as the data query.

              These conditions must match:

              • Search keyword and searched columns
              • Status, category and date filters
              • User ownership or role restrictions
              • Relevant JOIN conditions
              • Soft-delete conditions
              • Tenant or organization restrictions

              Counting every row in the table while displaying only active search results creates too many pagination links and empty final pages.

              SELECT COUNT(*)
              FROM products
              WHERE name LIKE ?
                AND status = ?;
              SELECT id, name, status
              FROM products
              WHERE name LIKE ?
                AND status = ?
              ORDER BY id DESC
              LIMIT ? OFFSET ?;

              Use Prepared Statements for Search Values

              Do not concatenate a browser-provided keyword directly into SQL. Create the wildcard pattern as a value and bind that value to a placeholder.

              $search = trim($_GET["search"] ?? "");
              $pattern = "%" . $search . "%";
              
              $stmt = $pdo->prepare(
                  "SELECT id, name
                   FROM products
                   WHERE name LIKE :search"
              );
              
              $stmt->execute([
                  "search" => $pattern
              ]);

              For a multiple-column search, bind the pattern separately for each placeholder rather than inserting the original keyword into the SQL string.

              Empty Search and LIKE %%

              Adding percent signs around an empty keyword creates LIKE '%%', which normally matches every non-null value. This may be correct when an empty search should show all records, but it is a bug when the page is supposed to require a keyword.

              Choose the behavior deliberately:

              • Show all records when search is empty.
              • Return no records until a keyword is entered.
              • Display a validation message requiring a keyword.

              Do not let an accidental empty pattern decide the product behavior.

              Use a Safe Sorting Allowlist

              Prepared-statement placeholders represent data values. They do not safely replace SQL column names or keywords in an ORDER BY clause. Therefore, user-selected sorting should map to server-controlled SQL.

              $allowedSorts = [
                  "newest" => "created_at DESC, id DESC",
                  "oldest" => "created_at ASC, id ASC",
                  "name" => "name ASC, id ASC"
              ];
              
              $orderBy = $allowedSorts[$sort]
                  ?? $allowedSorts["newest"];

              The unique ID used as a secondary order makes pagination more stable when multiple records share the same name or date.

              Why JOINs Produce Duplicate Records

              A parent row is repeated when it matches several rows in a joined child table. For example, one product with three tags may appear three times after joining the product-tag relationship.

              Possible solutions depend on the intended output:

              • Correct an accidental JOIN condition.
              • Use DISTINCT when the selected parent columns are identical.
              • Group by the parent record when calculating aggregates.
              • Load child collections in a separate query.
              • Count distinct parent IDs in the count query.

              Do not add DISTINCT blindly. First identify why one parent record matches multiple child rows and whether those relationships are meaningful.

              Stable Ordering Prevents Missing and Repeated Rows

              Pagination without ORDER BY does not define a reliable record order. Ordering only by a non-unique value, such as status or creation date, can also leave tied rows without a stable position.

              ORDER BY created_at DESC, id DESC

              Even with a stable order, new inserts and deletions between page requests can shift offset-based pagination. For ordinary student projects, stable offset pagination is usually acceptable. Larger real-time systems may use cursor-based pagination instead.

              AJAX Load More Must Advance One State

              A Load More feature must maintain one clear page or offset value. It should not increment both before and after a request, and it should not reset to page one when the search term changes unexpectedly.

              The response should tell the browser:

              • Which page was returned
              • How many rows were returned
              • Whether another page exists
              • The normalized active filters

              Disable the button while a request is pending to avoid two concurrent requests loading the same page.

              Safe Debugging Order

              1. Run the filtered data query without LIMIT and confirm the results.
              2. Run the filtered count query and compare its total.
              3. Validate the page and records-per-page values.
              4. Calculate and display the expected offset.
              5. Add stable ORDER BY, LIMIT and OFFSET.
              6. Compare returned IDs between page one and page two.
              7. Preserve all active query parameters in navigation links.
              8. Test sorting, search and filters together.
              9. Inspect JOINs when IDs are duplicated.
              10. Test empty, invalid and out-of-range page requests.

              Continue Your CodeZips PHP Project Workflow

              Technical References

              Frequently Asked Questions

              Why does page 2 show the same records as page 1?

              The calculated offset may not be included in the executed SQL, the offset may remain zero, or PHP may read the wrong page parameter. Use ($currentPage - 1) * $recordsPerPage.

              Why does my search disappear when I click page 2?

              The pagination link probably includes only the page parameter. Include the current keyword, status, category and sorting values in every page URL.

              Why is my total number of pages incorrect?

              The count query may not use the same WHERE and JOIN conditions as the query that loads displayed records.

              Why does an empty search return every row?

              Adding percent signs around an empty string creates a pattern such as %%. Decide explicitly whether empty search should show all rows, none or a validation message.

              Can I bind an ORDER BY column using a placeholder?

              Prepared placeholders represent data values, not SQL identifiers. Map the requested sort option to a server-controlled allowlist.

              Why does a JOIN duplicate the same record?

              One parent record may match several child rows. Inspect the relationship before choosing DISTINCT, GROUP BY, an aggregate query or a separate child query.

              Should pagination start at page zero or page one?

              User-facing pages normally start at one, while SQL OFFSET starts at zero. Page one therefore uses offset zero.

              Why does sorting change which records appear on each page?

              Pagination needs a stable ORDER BY. Add a unique secondary column such as the record ID when the main sort column can contain ties.

              Does this tool execute my SQL query?

              No. It analyzes the selected settings and visible code patterns locally in the browser. It does not connect to MySQL.

              Final Takeaway

              Reliable search pagination requires one consistent filtered dataset. Validate the keyword and page number, use prepared values, run a matching count query, calculate a zero-based offset, apply stable sorting and preserve every active filter in page links. Debug these stages separately instead of changing LIMIT values randomly.

              Leave a Comment

              Your email address will not be published. Required fields are marked *

              Scroll to Top