Pg result seek: примеры (PHP)

Работа с pg_result_seek: позиционирование в результатах PostgreSQL
Раздел: Базы данных (PostgreSQL)
pg_result_seek(PgSql\Result $result, int $row): bool

Описание функции pg_result_seek

Функция pg_result_seek используется для перемещения внутреннего указателя на произвольную строку в результате запроса к базе данных PostgreSQL.

Эта функция применяется, когда требуется повторно обработать определенные строки из уже полученного результата, не выполняя запрос заново. Это может быть полезно для навигации по данным, повторного извлечения строк или реализации пользовательской логики перебора.

Аргументы функции
  • $result (PgSql\Result|resource) - Обязательный. Ресурс результата запроса, возвращаемый функциями pg_query, pg_query_params или pg_execute.
  • $row (int) - Обязательный. Номер строки, на которую нужно переместить внутренний указатель. Нумерация строк начинается с 0.

Функция возвращает true в случае успеха и false в случае неудачи (например, при попытке установить указатель на несуществующую строку).

Короткие примеры использования

Пример 1: Перемещение к конкретной строке
$conn = pg_connect("host=localhost dbname=test user=postgres");
$result = pg_query($conn, "SELECT id, name FROM products ORDER BY id");

// Перемещаем указатель на 3-ю строку (индекс 2)
$seekResult = pg_result_seek($result, 2);
var_dump($seekResult);

// Извлекаем текущую строку (теперь это 3-я строка)
$row = pg_fetch_assoc($result);
print_r($row);
bool(true)
Array
(
    [id] => 3
    [name] => Product C
)
Пример 2: Попытка перемещения к несуществующей строке
$result = pg_query($conn, "SELECT id FROM products LIMIT 5");
$seekResult = pg_result_seek($result, 10);
var_dump($seekResult);
bool(false)

Альтернативные функции в PHP

pg_fetch_row с указанием индекса

Можно сразу извлекать строку по индексу, используя pg_fetch_row, pg_fetch_assoc или pg_fetch_array с указанием номера строки в качестве второго аргумента. Этот подход не меняет внутренний указатель результата.

$row = pg_fetch_assoc($result, 3); // Получить 4-ю строку
Получение всех строк в массив

Использование pg_fetch_all позволяет получить все строки как массив, после чего навигация осуществляется стандартными средствами PHP.

$allRows = pg_fetch_all($result);
$specificRow = $allRows[2]; // Доступ к 3-й строке

Предпочтения: pg_result_seek удобна для сценариев с последовательным доступом к разным частям результата. Прямое извлечение по индексу эффективнее для разовых операций. Получение всего массива потребляет больше памяти, но упрощает сложную навигацию.

Аналоги в других языках и СУБД

Python (psycopg2)

В psycopg2 курсор по умолчанию представляет собой итератор, но можно использовать scroll() для перемещения.

cursor.execute("SELECT * FROM products")
cursor.scroll(2, mode='absolute') # Перемещение к 3-й записи
row = cursor.fetchone()
JavaScript (Node.js с pg)

Модуль 'pg' обычно возвращает все строки сразу в массиве. Навигация происходит по этому массиву.

const result = await pool.query('SELECT * FROM products');
const thirdRow = result.rows[2];
MySQL (в запросе)

Вместо позиционирования на клиенте часто используют OFFSET в SQL-запросе.

-- Получить 3-ю строку (нумерация с 0)
SELECT * FROM products LIMIT 1 OFFSET 2;

Отличия: pg_result_seek работает на стороне клиента с уже полученными данными, в то время как OFFSET и LIMIT выполняются на сервере. Это влияет на производительность при работе с большими объемами данных.

Типичные ошибки

Ошибка 1: Неверный тип ресурса
$conn = pg_connect("host=localhost dbname=test");
// ОШИБКА: Первый аргумент - не результат запроса
$seekResult = pg_result_seek($conn, 0);
var_dump($seekResult);
bool(false)
Ошибка 2: Смещение за пределами результата
$result = pg_query($conn, "SELECT id FROM products LIMIT 2");
$seekResult = pg_result_seek($result, 5); // Всего 2 строки
if (!$seekResult) {
    echo "Не удалось переместить указатель. Проверьте номер строки.";
}
Ошибка 3: Использование с пустым результатом
$result = pg_query($conn, "SELECT id FROM products WHERE 1=0");
$seekResult = pg_result_seek($result, 0); // Результат пуст
var_dump($seekResult);
bool(false)

Изменения в последних версиях PHP

В PHP 8.0 тип параметра $result был изменен с resource на PgSql\Result в рамках общей политики типизации ресурсов. Старый псевдоним типа resource по-прежнему принимается для обратной совместимости, но рекомендуется использовать новый тип.

Начиная с PHP 8.1, при передаче некорректного типа аргумента генерируется ошибка TypeError уровня E_WARNING, а не возвращается false молча, как в более ранних версиях.

Значительных изменений в поведении функции в версиях PHP 8.2 и 8.3 не было.

Расширенные примеры использования

Пример 1: Циклическая навигация по результату
Пример php
$result = pg_query($conn, "SELECT id, name FROM products ORDER BY id");
$totalRows = pg_num_rows($result);

// Проход вперед
for ($i = 0; $i < $totalRows; $i++) {
    pg_result_seek($result, $i);
    $row = pg_fetch_assoc($result);
    echo $row['id'] . ': ' . $row['name'] . "\n";
}

// Проход в обратном порядке
for ($i = $totalRows - 1; $i >= 0; $i--) {
    pg_result_seek($result, $i);
    $row = pg_fetch_assoc($result);
    echo $row['id'] . ': ' . $row['name'] . "\n";
}
Пример 2: Извлечение строк по паттерну
Пример php
$result = pg_query($conn, "SELECT id, status FROM orders");
$pendingOrders = [];
$totalRows = pg_num_rows($result);

for ($i = 0; $i < $totalRows; $i++) {
    pg_result_seek($result, $i);
    $row = pg_fetch_assoc($result);
    if ($row['status'] === 'pending') {
        $pendingOrders[] = $row;
        // Вернуться и проверить предыдущую строку?
        // pg_result_seek($result, $i-1); // Пример условного возврата
    }
}
print_r($pendingOrders);
Пример 3: Имитация кэширования строк
Пример php
$queryCache = [];
function getCachedRow($result, $index) {
    global $queryCache;
    $resultId = (int)$result; // Упрощенный идентификатор
    
    if (!isset($queryCache[$resultId][$index])) {
        if (pg_result_seek($result, $index)) {
            $queryCache[$resultId][$index] = pg_fetch_assoc($result);
        }
    }
    return $queryCache[$resultId][$index] ?? null;
}

$result = pg_query($conn, "SELECT * FROM large_table LIMIT 100");
$row10 = getCachedRow($result, 9); // Загрузит и закэширует
$row10Again = getCachedRow($result, 9); // Возьмет из кэша
Пример 4: Работа с частично обработанным результатом
Пример php
$result = pg_query($conn, "SELECT * FROM log_messages ORDER BY created_at");

// Обработали первые 5 записей
for ($i = 0; $i < 5; $i++) {
    $row = pg_fetch_assoc($result);
    processLogEntry($row);
}

// Позже вернулись к 3-й записи для повторной обработки
if (pg_result_seek($result, 2)) {
    $specificLog = pg_fetch_assoc($result);
    reanalyzeEntry($specificLog);
}

// Продолжили с 6-й записи (указатель сейчас на 4 после последнего fetch)
// Нужно снова переместить указатель
pg_result_seek($result, 5);
while ($row = pg_fetch_assoc($result)) {
    processLogEntry($row);
}

PHP pg_result_seek function comments

En
Pg result seek Set internal row offset in result resource