data_seek
In this article, we will focus on the mysqli_data_seek() function in PHP, which is used to move the result pointer to a specified row number in a result set. We
The mysqli_data_seek() function in PHP moves the internal result pointer to an arbitrary row in a buffered result set, so the next fetch call reads from that row instead of the next sequential one. It lets you jump directly to a row, re-read rows you have already passed, or restart from the beginning without running the query again. Row indices are zero-based, and the function returns true on success or false on failure.
This chapter covers the function signature, both the procedural and object-oriented styles, a runnable example, the buffered-result requirement that trips most people up, and when a SQL LIMIT … OFFSET is the better tool.
Syntax
// Procedural style
mysqli_data_seek(mysqli_result $result, int $offset): bool
// Object-oriented style
$result->data_seek(int $offset): bool$result— a result set returned bymysqli_query(),mysqli_store_result(), ormysqli_use_result().$offset— the row number to move to, starting at0for the first row. It must be between0andmysqli_num_rows() - 1.- Return value —
trueif the seek succeeds,falseif the offset is out of range or the result set is unbuffered.
The buffered-result requirement
mysqli_data_seek() only works on buffered result sets — ones whose rows are already held in memory. That is what you get from mysqli_query() (which calls mysqli_store_result() internally) and from mysqli_store_result() directly.
If you fetch rows lazily with mysqli_use_result(), the rows stream from the server one at a time and there is nothing to seek into, so mysqli_data_seek() will fail. When you need random access, stick with a buffered result.
How to use mysqli_data_seek()
Call the function on a valid result set and pass the row index you want to land on. The next fetch then returns that row:
<?php
$mysqli = mysqli_connect("localhost", "username", "password", "database");
$result = mysqli_query($mysqli, "SELECT id, name FROM users ORDER BY id");
if (!$result) {
echo "Failed to execute query: " . mysqli_error($mysqli);
exit();
}
// Move the pointer to row 3 (index 2, because indices are zero-based)
if (!mysqli_data_seek($result, 2)) {
echo "Seek failed";
exit();
}
// Fetch the row the pointer now points at
$row = mysqli_fetch_assoc($result);
print_r($row);
mysqli_free_result($result);
mysqli_close($mysqli);
?>We connect with mysqli_connect(), run the query with mysqli_query(), and check for errors. Then mysqli_data_seek($result, 2) jumps the pointer to the third row, and mysqli_fetch_assoc() reads it. Checking the return value of mysqli_data_seek() lets you handle an out-of-range offset cleanly.
Re-reading a result set from the start
A common, database-free way to see exactly how the pointer behaves is to walk a result, then seek back to 0 and walk it again. This snippet uses a plain PHP array to model the same fetch-then-seek logic without needing a live MySQL server:
<?php
// A result set modelled as an in-memory array of rows.
$rows = [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob'],
['id' => 3, 'name' => 'Carol'],
];
$pointer = 0;
// "data_seek": move the pointer to an arbitrary index, like mysqli_data_seek().
function data_seek(array $rows, int $offset, int &$pointer): bool
{
if ($offset < 0 || $offset >= count($rows)) {
return false;
}
$pointer = $offset;
return true;
}
// First pass: read every row sequentially.
echo "First pass:\n";
while ($pointer < count($rows)) {
echo $rows[$pointer]['name'] . "\n";
$pointer++;
}
// Rewind to the top and read again.
data_seek($rows, 0, $pointer);
echo "Second pass (after seek to 0):\n";
echo $rows[$pointer]['name'] . "\n"; // Alice again
?>This prints:
First pass:
Alice
Bob
Carol
Second pass (after seek to 0):
AliceWith a real mysqli_result, you would replace the array logic with mysqli_data_seek($result, 0) followed by mysqli_fetch_assoc($result) to rewind a buffered result without re-running the query.
When to use LIMIT/OFFSET instead
mysqli_data_seek() is for navigating a result set you have already downloaded. If your goal is to fetch only a slice of a large table, do not pull every row into PHP just to seek — push the work to the database with LIMIT and OFFSET:
SELECT id, name FROM users ORDER BY id LIMIT 10 OFFSET 20;This returns only the 10 rows you want, saving memory and network traffic. Reserve mysqli_data_seek() for cases where you genuinely need random access across a result set that is already in memory — for example, re-reading earlier rows during a single request.
Conclusion
mysqli_data_seek() repositions the internal pointer of a buffered result set so you can jump to any zero-based row, including back to the start. Remember its two constraints: the offset must be in range (0 … num_rows - 1), and the result must be buffered. For trimming large datasets, prefer SQL LIMIT/OFFSET; for moving around data already in memory, mysqli_data_seek() is the right tool.
Related functions: mysqli_fetch_assoc(), mysqli_fetch_array(), mysqli_fetch_row(), and mysqli_query().