Я использую PHP 5.3.6 с PDO для доступа к Postgres 9.0.4. Меня попросили уменьшить объем памяти в отчете. Текущая реализация проста: выполните запрос, выполните fetchAll (), а затем итерайте с помощью foreach () через результирующий массив. Это, очевидно, не масштабируется с огромными наборами результатов: он может временно потреблять 100 МБ или более.
У меня есть новая реализация, которая обрабатывает дескриптор инструкции PDO, а затем выполняет прямое обращение к ней с помощью foreach (), то есть без промежуточного массива через fetchAll (). (Из того, что я прочитал, итерация дескриптора инструкции с помощью foreach вызывает fetch () под обложками.) Это так же быстро и потребляет меньше памяти: около 28 КБ. Тем не менее, я не уверен, что я делаю это правильно, потому что, хотя я сделал тонну Googling, трудно найти ответы на основные вопросы об этом:
Я видел статьи, которые предлагают решить мою оригинальную проблему с помощью курсоров. Включает ли драйвер Post Office PDO внутренние курсоры? Если писать собственный SQL для создания курсора требуется, я готов, но я бы предпочел написать самый простой код (но не проще!).
Если foreach вызывает fetch () каждую итерацию, разве это не слишком чат в сети? Или он умный и извлекает сразу несколько строк, например 500, чтобы сохранить пропускную способность? (Это может означать, что он использует курсоры внутри.)
Я видел статью, которая обертывает дескриптор оператора в классе, который реализует интерфейс Iterator. Разве это не избыточно, учитывая, что дескриптор инструкции PDO уже делает это? Или я чего-то не хватает?
Мой вызов подготовить инструкцию SQL выглядит следующим образом:
$ sth = $ dbh-> prepare ($ sql);
Я обнаружил, что это не изменило память или скорость, если я сделал это:
$sth = $dbh->prepare($sql, array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );
Это потому, что это по умолчанию для драйвера Postgres PDO? Это имеет смысл, если он уже использует курсоры внутри.
Приветствуются общие замечания относительно подхода и других способов решения этой проблемы.
PDO для Postgres использует внутренние курсоры .
По-видимому, PDO::CURSOR_FWDONLY
не использует курсоры. Тесты черного ящика:
(0) Препараты:
$con = new \PDO('dsn'); // you'll get "NO ACTIVE TRANSACTION" otherwise $con->beginTransaction(); $sql = 'select * from largetable';
(1) По умолчанию – навсегда:
$stmt = $con->prepare($sql); $stmt->execute(); print_r($stmt->fetch());
(2) FWDONLY – берет навсегда:
$stmt = $con->prepare($sql, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY)); $stmt->execute(); print_r($stmt->fetch());
(3) SCROLLABLE – работает мгновенно:
$stmt = $con->prepare($sql, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL)); $stmt->execute(); print_r($stmt->fetch());
Я включил ведение журнала PG, чтобы быть уверенным, и это действительно так – только SCROLL использует курсоры.
Таким образом, единственный способ использовать курсоры – использовать SCROLL, по крайней мере, в PHP 5.4.23.