Массивы результатов PHP: способы извлечения и обработки данных

Раздел: PHP программирование -> Обработка данных

Основные подходы к работе с массивами результатов

Как получить все строки результата запроса в виде массива, используя PDO::fetchAll?

Метод fetchAll является наиболее эффективным решением для получения всех строк результата запроса сразу в виде массива. По умолчанию возвращается массив, каждый элемент которого - также массив (по умолчанию нумерованный). Однако чаще всего требуется ассоциативный массив с именами столбцов. Для этого используется константа PDO::FETCH_ASSOC.


<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT id, name, email FROM users');
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($users);
?>

Php function parse (парсинг в php)

Результат - массив вида [ ['id'=>1,'name'=>'Иван','email'=>'ivan@mail.com'], [...] ]. Это удобно для дальнейшей обработки в циклах или для передачи в шаблонизатор.

Проблемы и ошибки: если запрос не вернул ни одной строки, fetchAll вернет пустой массив, а не false. Это может привести к ошибкам, если код ожидает false. Также при работе с очень большими наборами данных (десятки тысяч строк) вызов fetchAll может привести к переполнению памяти - в таких случаях лучше использовать итеративный fetch.

Как получить результат в виде нумерованного массива при помощи PDO::FETCH_NUM?

Использование PDO::FETCH_NUM возвращает каждую строку как индексный массив. Это может быть полезно, если имена столбцов не важны, а важна последовательность полей.


<?php
$stmt = $pdo->query('SELECT id, name FROM users');
$rows = $stmt->fetchAll(PDO::FETCH_NUM);
// $rows[0][0] - id, $rows[0][1] - name
?>

Result array php (массив результатов php)

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

Как получить только один столбец из результата в виде плоского массива?

Метод fetchAll(PDO::FETCH_COLUMN, 0) выбирает только указанный столбец (по номеру). В результате получается одномерный массив значений.


<?php
$stmt = $pdo->query('SELECT email FROM users WHERE active=1');
$emails = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
print_r($emails); // ['ivan@mail.com', 'petr@mail.ru', ...]
?>

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

Типичная ошибка: указание неверного индекса столбца. В PDO столбцы нумеруются с 0. Если запрос содержит SELECT *, номер столбца может быть неизвестен.

Как организовать пошаговое чтение строк (итеративный fetch) для экономии памяти?

Вместо fetchAll применяется цикл while с вызовом fetch. Каждая строка обрабатывается и удаляется из памяти сразу. Это незаменимо при работе с большими таблицами.


<?php
$stmt = $pdo->query('SELECT * FROM logs');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    // обработать одну строку
    echo $row['id'] . ' - ' . $row['message'] . "\n";
}
?>

Вопрос производительности: при таком подходе не накапливается память, но каждое обращение к базе создает сетевой трафик. Для очень больших наборов может потребоваться использование курсора (на стороне БД).

Как получить массив результатов из mysqli?

В библиотеке mysqli для этой цели используется метод fetch_all (доступен только при установленном драйвере mysqlnd). Результат - массив строк.


<?php
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$result = $mysqli->query('SELECT id, name FROM users');
$users = $result->fetch_all(MYSQLI_ASSOC);
print_r($users);
?>

Альтернатива - цикл while ($row = $result->fetch_assoc()), который не требует mysqlnd и работает медленнее, но более портабельно.

Ошибка: если сервер не поддерживает mysqlnd, вызов fetch_all приведет к фатальной ошибке. В таком случае нужно использовать цикл fetch_assoc.

Как получить массив объектов с помощью PDO::FETCH_OBJ?

Иногда удобнее работать не с массивами, а с объектами стандартного класса stdClass. PDO::FETCH_OBJ превращает каждую строку в объект со свойствами, соответствующими именам столбцов.


<?php
$stmt = $pdo->query('SELECT id, name FROM users');
$users = $stmt->fetchAll(PDO::FETCH_OBJ);
foreach ($users as $user) {
    echo $user->name;
}
?>

Этот способ удобен для передачи данных в шаблоны или при использовании методов объекта.

Минус: свойство объекта не допускает пробелов и специальных символов. Если имя поля содержит недопустимые символы, нужно использовать массивы.

Практические примеры с подробными пояснениями

Пример 1: Фильтрация и обработка данных после fetchAll

Пример

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT id, name, email FROM users');
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Отфильтруем только пользователей с email, содержащим "@company.com"
$companyUsers = array_filter($users, function($u) {
    return strpos($u['email'], '@company.com') !== false;
});

// Извлечем только их имена
$names = array_column($companyUsers, 'name');
print_r($names);
?>
Array
(
    [0] => Петр
    [1] => Анна
)

Здесь показано, как комбинировать fetchAll со встроенными функциями PHP для последующей фильтрации и трансформации данных.

Пример 2: Постраничная навигация с помощью fetchAll и array_slice

Пример

<?php
$page = $_GET['page'] ?? 1;
$perPage = 10;
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT * FROM articles');
$allArticles = $stmt->fetchAll(PDO::FETCH_ASSOC);

$offset = ($page - 1) * $perPage;
$pageArticles = array_slice($allArticles, $offset, $perPage);

foreach ($pageArticles as $article) {
    echo $article['title'] . "<br>";
}
?>

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

Пример 3: Группировка результатов в многомерный массив по полю (с использованием PDO::FETCH_GROUP)

Пример

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT role, name FROM users');
// Группируем по полю 'role', массив ключей - значения первого столбца
$grouped = $stmt->fetchAll(PDO::FETCH_COLUMN | PDO::FETCH_GROUP);
print_r($grouped);
?>
Array
(
    [admin] => Array (
        [0] => Иван
        [1] => Петр
    )
    [user] => Array (
        [0] => Анна
        [1] => Ольга
    )
)

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

Пример 4: Работа с большим набором данных - итеративный fetch с буферизацией

Пример

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
]);
$stmt = $pdo->query('SELECT id, huge_text FROM logs');
$count = 0;
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    // Эмулируем обработку каждой 1000-й строки
    if (++$count % 1000 == 0) {
        echo "Обработано $count строк\n";
        flush();
    }
}
?>

Отключение буферизации (MYSQL_ATTR_USE_BUFFERED_QUERY => false) позволяет не загружать весь результат в память сервера PHP, но требует больше сетевых операций. Применяется в долгих задачах или экспорте.

Пример 5: Использование fetchAll с пользовательским классом (PDO::FETCH_CLASS)

Пример

<?php
class User {
    public $id;
    public $name;
    public $email;
}

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT id, name, email FROM users');
$users = $stmt->fetchAll(PDO::FETCH_CLASS, 'User');
// Каждый элемент массива - экземпляр класса User
foreach ($users as $user) {
    echo $user->name . ' <' . $user->email . '>';
}
?>

Объектно-ориентированный подход упрощает дальнейшую работу с данными, особенно если в классе есть методы (валидация, форматирование).

Пример 6: Преобразование результата mysqli_fetch_all в случае отсутствия mysqlnd

Пример

<?php
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$result = $mysqli->query('SELECT id, name FROM users');
$rows = [];
while ($row = $result->fetch_assoc()) {
    $rows[] = $row;
}
print_r($rows);
?>
Array
(
    [0] => Array ( [id] => 1, [name] => Иван )
    [1] => Array ( [id] => 2, [name] => Петр )
)

Этот код всегда работает, независимо от наличия mysqlnd. Недостаток - больше кода и меньшая производительность по сравнению с нативной fetch_all.

Массив результатов PHP - comments

En
Result array php (php)