Spec-Zone.ru › Yii 2.0

Построитель запросов

  1. Создание запросов
  2. Методы запросов

Основываясь на объектах доступа к базе данных, построитель запросов позволяет создавать SQL-запросы программным способом и независимо от СУБД. По сравнению с написанием исходных SQL-команд, использование построителя запросов поможет вам писать более читаемый код, связанный с SQL, и генерировать более безопасные SQL-команды.

Использование построителя запросов обычно включает два шага:

  1. Создайте объект yii\db\Query, чтобы представить различные части (например, SELECT, FROM) SELECT SQL-запроса.
  2. Выполните метод запроса (например, all()) объекта yii\db\Query для извлечения данных из базы данных.

Следующий код демонстрирует типичный способ использования построителя запросов:

$rows = (new \yii\db\Query())
    ->select(['id', 'email'])
    ->from('user')
    ->where(['last_name' => 'Smith'])
    ->limit(10)
    ->all();

Вышеприведенный код генерирует и выполняет следующий SQL-запрос, где параметр :last_name связан со строкой 'Smith'.

SELECT `id`, `email` 
FROM `user`
WHERE `last_name` = :last_name
LIMIT 10

Информация: Обычно вы преимущественно работаете с yii\db\Query, а не с yii\db\QueryBuilder. Последний вызывается первым неявно, когда вы вызываете один из методов запроса. yii\db\QueryBuilder — это класс, ответственный за генерацию SQL-запросов, зависящих от СУБД (например, по-разному заключая в кавычки имена таблиц/столбцов), из независимых от СУБД объектов yii\db\Query.

Создание запросов

Чтобы создать объект yii\db\Query, вы вызываете различные методы построения запросов, чтобы указать различные части SQL-запроса. Названия этих методов напоминают SQL-ключевые слова, используемые в соответствующих частях SQL-запроса. Например, чтобы указать часть FROM SQL-запроса, вы вызовите метод from(). Все методы построения запросов возвращают сам объект запроса, что позволяет объединять несколько вызовов.

В дальнейшем мы опишем использование каждого метода построения запросов.

select()

Метод select() определяет фрагмент SELECT SQL-запроса. Вы можете указать столбцы для выбора либо в виде массива, либо в виде строки, как показано ниже. Имена столбцов, которые выбираются, будут автоматически заключены в кавычки при генерации SQL-запроса из объекта запроса.

$query->select(['id', 'email']);

// equivalent to:

$query->select('id, email');

Имена столбцов, которые выбираются, могут включать префиксы таблиц и/или псевдонимы столбцов, как вы делаете при написании исходных SQL-запросов. Например,

$query->select(['user.id AS user_id', 'email']);

// equivalent to:

$query->select('user.id AS user_id, email');

Если вы используете массив для указания столбцов, вы также можете использовать ключи массива для указания псевдонимов столбцов. Например, вышеприведенный код можно переписать следующим образом,

$query->select(['user_id' => 'user.id', 'email']);

Если вы не вызываете метод select() при построении запроса, * будут выбраны, что означает выбор всех столбцов.

Помимо имен столбцов, вы также можете выбирать выражения базы данных. Вы должны использовать формат массива при выборе выражения базы данных, содержащего запятые, чтобы избежать неправильного автоматического заключения в кавычки имен. Например,

$query->select(["CONCAT(first_name, ' ', last_name) AS full_name", 'email']); 

Как и во всех местах, где участвует исходный SQL, вы можете использовать синтаксис цитирования, независимый от СУБД, для имен таблиц и столбцов при написании выражений базы данных в select.

Начиная с версии 2.0.1, вы также можете выбирать подзапросы. Вы должны указать каждый подзапрос в терминах объекта yii\db\Query. Например,

$subQuery = (new Query())->select('COUNT(*)')->from('user');

// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`
$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');

Чтобы выбрать различные строки, вы можете вызвать distinct(), как показано ниже:

// SELECT DISTINCT `user_id` ...
$query->select('user_id')->distinct();

Вы можете вызвать addSelect() для выбора дополнительных столбцов. Например,

$query->select(['id', 'username'])
    ->addSelect(['email']);

from()

Метод from() определяет фрагмент FROM SQL-запроса. Например,

// SELECT * FROM `user`
$query->from('user');

Вы можете указать таблицу(ы), из которых осуществляется выбор, либо в виде строки, либо в виде массива. Имена таблиц могут содержать префиксы схем и/или псевдонимы таблиц, как вы делаете при написании исходных SQL-запросов. Например,

$query->from(['public.user u', 'public.post p']);

// equivalent to:

$query->from('public.user u, public.post p');

Если вы используете массив, вы также можете использовать ключи массива для указания псевдонимов таблиц, как показано ниже:

$query->from(['u' => 'public.user', 'p' => 'public.post']);

Помимо имен таблиц, вы также можете выбирать из подзапросов, указав их в терминах объектов yii\db\Query. Например,

$subQuery = (new Query())->select('id')->from('user')->where('status=1');

// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u 
$query->from(['u' => $subQuery]);

Префиксы

Также может применяться значение по умолчанию tablePrefix. Инструкции по реализации находятся в "Раздел "Цитаты таблиц" руководства по "Объектам доступа к базе данных".

where()

Метод where() определяет фрагмент WHERE SQL-запроса. Вы можете использовать один из трех форматов для указания условия WHERE:

  • строковый формат, например, 'status=1'
  • формат хеша, например, ['status' => 1, 'type' => 2]
  • формат оператора, например, ['like', 'name', 'test']

Строковый формат

Строковый формат лучше использовать для указания очень простых условий или если вам нужны встроенные функции СУБД. Он работает так, как если бы вы писали исходный SQL. Например,

$query->where('status=1');

// or use parameter binding to bind dynamic parameter values
$query->where('status=:status', [':status' => $status]);

// raw SQL using MySQL YEAR() function on a date field
$query->where('YEAR(somedate) = 2015');

НЕ ВКЛЮЧАЙТЕ переменные непосредственно в условие, как в следующем примере, особенно если значения переменных поступают от конечного пользователя, так как это подвергнет ваше приложение атакам SQL-инъекций.

// Dangerous! Do NOT do this unless you are very certain $status must be an integer.
$query->where("status=$status");

При использовании связывания параметров вы можете вызвать params() или addParams() для отдельного указания параметров.

$query->where('status=:status')
    ->addParams([':status' => $status]);

Как и во всех местах, где участвует исходный SQL, вы можете использовать синтаксис цитирования, независимый от СУБД, для имен таблиц и столбцов при написании условий в строковом формате.

Формат хеша

Формат хеша лучше всего использовать для указания множества AND-связанных подслов, каждое из которых является простым утверждением равенства. Он записывается как массив, ключами которого являются имена столбцов, а значениями — соответствующие значения, которые должны иметь столбцы. Например,

// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))
$query->where([
    'status' => 10,
    'type' => null,
    'id' => [4, 8, 15],
]);

Как видите, построитель запросов достаточно интеллектуален, чтобы корректно обрабатывать значения, которые являются null или массивами.

Вы также можете использовать подзапросы с форматом хеша, как показано ниже:

$userQuery = (new Query())->select('id')->from('user');

// ...WHERE `id` IN (SELECT `id` FROM `user`)
$query->where(['id' => $userQuery]);

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

Формат оператора

Формат оператора позволяет указывать произвольные условия программно. Он принимает следующий формат:

[operator, operand1, operand2, ...]

где операнды могут быть указаны в строковом формате, формате хеша или формате оператора рекурсивно, а оператор может быть одним из следующих:

  • and: операнды должны быть соединены вместе с помощью AND. Например, ['and', 'id=1', 'id=2'] сгенерирует id=1 AND id=2. Если операнд является массивом, он будет преобразован в строку по правилам, описанным здесь. Например, ['and', 'type=1', ['or', 'id=1', 'id=2']] сгенерирует type=1 AND (id=1 OR id=2). Метод НЕ будет выполнять цитирование или экранирование.

  • or: аналогично оператору and, за исключением того, что операнды соединяются с помощью OR.

  • between: операнд 1 должен быть именем столбца, а операнды 2 и 3 — начальным и конечным значениями диапазона, в котором находится столбец. Например, ['between', 'id', 1, 10] сгенерирует id BETWEEN 1 AND 10.

  • not between: аналогично between, за исключением того, что BETWEEN в сгенерированном условии заменяется на NOT BETWEEN.

  • in: операнд 1 должен быть именем столбца или выражением БД. Операнд 2 может быть либо массивом, либо объектом Query. Он сгенерирует условие IN. Если операнд 2 является массивом, он представляет диапазон значений, которые должен принимать столбец или выражение БД; если операнд 2 является объектом Query, будет сгенерирована подзапрос, используемый как диапазон столбца или выражения БД. Например, ['in', 'id', [1, 2, 3]] сгенерирует id IN (1, 2, 3). Метод должным образом процитирует имя столбца и экранирует значения в диапазоне. Оператор in также поддерживает составные столбцы. В этом случае операнд 1 должен быть массивом столбцов, а операнд 2 — массивом массивов или объектом Query, представляющим диапазон столбцов.

  • not in: аналогично оператору in, за исключением того, что IN в сгенерированном условии заменяется на NOT IN.

  • like: операнд 1 должен быть именем столбца или выражением БД, а операнд 2 — строкой или массивом, представляющим значения, которые должен принимать столбец или выражение БД. Например, ['like', 'name', 'tester'] сгенерирует name LIKE '%tester%'. Когда диапазон значений задан как массив, несколько предикатов LIKE будут сгенерированы и соединены с помощью AND. Например, ['like', 'name', ['test', 'sample']] сгенерирует name LIKE '%test%' AND name LIKE '%sample%'. Вы также можете указать необязательный третий операнд для указания способа экранирования специальных символов в значениях. Операнд должен быть массивом сопоставлений из специальных символов и их экранированных аналогов. Если этот операнд не указан, будет использоваться стандартное отображение экранирования. Вы можете использовать false или пустой массив, чтобы указать, что значения уже экранированы и не требуют экранирования. Обратите внимание, что при использовании отображения экранирования (или если третий операнд не указан), значения автоматически будут заключены в пару символов процента.

    Примечание: При использовании PostgreSQL вы также можете использовать ilike вместо like для выполнения нечувствительного к регистру соответствия.

  • or like: аналогично оператору like, за исключением того, что OR используется для объединения предикатов LIKE при наличии массива в качестве операнда 2.

  • not like: аналогично оператору like, за исключением того, что LIKE заменяется на NOT LIKE в сгенерированном условии.

  • or not like: аналогично оператору not like, за исключением того, что OR используется для объединения предикатов NOT LIKE.

  • exists: требует одного операнда, который должен быть экземпляром yii\db\Query, представляющим подзапрос. Он построит выражение EXISTS (sub-query).

  • not exists: аналогично оператору exists и строит выражение NOT EXISTS (sub-query).

  • >, <=, или любой другой допустимый оператор БД, принимающий два операнда: первый операнд должен быть именем столбца, а второй — значением. Например, ['>', 'age', 10] сгенерирует age>10.

Используя формат оператора, Yii внутренне использует привязку параметров, поэтому в отличие от формата строк, вам не нужно добавлять параметры вручную.

Добавление условий

Вы можете использовать andWhere() или orWhere() для добавления дополнительных условий к существующему. Вы можете вызывать их многократно, чтобы добавить несколько условий по отдельности. Например,

$status = 10;
$search = 'yii';

$query->where(['status' => $status]);

if (!empty($search)) {
    $query->andWhere(['like', 'title', $search]);
}

Если $search не пусто, то будет сгенерировано следующее условие WHERE:

WHERE (`status` = 10) AND (`title` LIKE '%yii%')

Условные фильтры

При создании условий WHERE на основе входных данных от конечных пользователей, вы обычно хотите игнорировать пустые значения. Например, в форме поиска, которая позволяет искать по имени пользователя и электронной почте, вы хотите игнорировать условие по имени пользователя/электронной почте, если пользователь ничего не вводит в соответствующее поле. Вы можете добиться этого, используя метод filterWhere():

// $username and $email are from user inputs
$query->filterWhere([
    'username' => $username,
    'email' => $email,
]);

Единственное отличие между filterWhere() и where() состоит в том, что первый игнорирует пустые значения, предоставленные в условии в формате hash. Таким образом, если $email пусто, а $username — нет, приведенный код приведет к условию SQL WHERE username=:username.

Информация: Значение считается пустым, если оно null, является пустым массивом, пустой строкой или строкой, содержащей только пробелы.

Так же, как andWhere() и orWhere(), вы можете использовать andFilterWhere() и orFilterWhere() для добавления дополнительных фильтрующих условий к существующему.

Кроме того, существует yii\db\Query::andFilterCompare(), который может интеллектуально определить оператор на основе значения:

$query->andFilterCompare('name', 'John Doe');
$query->andFilterCompare('rating', '>9');
$query->andFilterCompare('value', '<=100');

Вы также можете явно указать оператор:

$query->andFilterCompare('name', 'Doe', 'like');

Начиная с Yii 2.0.11, существуют аналогичные методы для HAVING условий:

  • filterHaving()
  • andFilterHaving()
  • orFilterHaving()

orderBy()

Метод orderBy() задаёт фрагмент ORDER BY SQL-запроса. Например,

// ... ORDER BY `id` ASC, `name` DESC
$query->orderBy([
    'id' => SORT_ASC,
    'name' => SORT_DESC,
]);

В приведенном коде ключи массива — имена столбцов, а значения — соответствующие направления сортировки. PHP-константа SORT_ASC задаёт сортировку по возрастанию, а SORT_DESC — по убыванию.

Если ORDER BY включает только простые имена столбцов, вы можете указать его в виде строки, как и при написании SQL-запросов. Например,

$query->orderBy('id ASC, name DESC');

Примечание: Вы должны использовать формат массива, если ORDER BY включает в себя какие-либо выражения БД.

Вы можете вызвать addOrderBy(), чтобы добавить дополнительные столбцы к фрагменту ORDER BY. Например,

$query->orderBy('id ASC')
    ->addOrderBy('name DESC');

groupBy()

Метод groupBy() задаёт фрагмент GROUP BY SQL-запроса. Например,

// ... GROUP BY `id`, `status`
$query->groupBy(['id', 'status']);

Если GROUP BY включает только простые имена столбцов, вы можете указать его в виде строки, как и при написании SQL-запросов. Например,

$query->groupBy('id, status');

Примечание: Вы должны использовать формат массива, если GROUP BY включает в себя какие-либо выражения БД.

Вы можете вызвать addGroupBy() для добавления дополнительных столбцов к фрагменту GROUP BY. Например,

$query->groupBy(['id', 'status'])
    ->addGroupBy('age');

having()

Метод having() задаёт фрагмент HAVING SQL-запроса. Он принимает условие, которое может быть задано так же, как и для where(). Например,

// ... HAVING `status` = 1
$query->having(['status' => 1]);

Для получения дополнительной информации о том, как задать условие, обратитесь к документации по where().

Вы можете вызвать andHaving() или orHaving() для добавления дополнительных условий к фрагменту HAVING. Например,

// ... HAVING (`status` = 1) AND (`age` > 30)
$query->having(['status' => 1])
    ->andHaving(['>', 'age', 30]);

limit() и offset()

Методы limit() и offset() задают фрагменты LIMIT и OFFSET SQL-запроса. Например,

// ... LIMIT 10 OFFSET 20
$query->limit(10)->offset(20);

Если вы укажете недопустимый limit или offset (например, отрицательное значение), он будет проигнорирован.

Информация: Для СУБД, которые не поддерживают LIMIT и OFFSET (например, MSSQL), генератор запросов сгенерирует SQL-запрос, который эмулирует поведение LIMIT/OFFSET.

join()

Метод join() задаёт фрагмент JOIN SQL-запроса. Например,

// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`
$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');

Метод join() принимает четыре параметра:

  • $type: тип соединения, например, 'INNER JOIN', 'LEFT JOIN'.
  • $table: имя таблицы, с которой нужно выполнить соединение.
  • $on: необязательно, условие соединения, т.е. фрагмент ON. Для подробностей о способах задания условия см. where(). Обратите внимание, что синтаксис массивов не подходит для задания условия по столбцу, например, ['user.id' => 'comment.userId'] приведет к условию, где идентификатор пользователя должен быть равен строке 'comment.userId'. Используйте вместо этого строковый синтаксис и задайте условие как 'user.id = comment.userId'.
  • $params: необязательно, параметры, которые нужно привязать к условию соединения.

Вы можете использовать следующие сокращенные методы для задания INNER JOIN, LEFT JOIN и RIGHT JOIN, соответственно.

  • innerJoin()
  • leftJoin()
  • rightJoin()

Например,

$query->leftJoin('post', 'post.user_id = user.id');

Для соединения с несколькими таблицами вызовите вышеуказанные методы соединения по одному для каждой таблицы.

Помимо соединения с таблицами, вы также можете соединяться с подзапросами. Для этого укажите подзапросы для соединения в качестве объектов yii\db\Query. Например,

$subQuery = (new \yii\db\Query())->from('post');
$query->leftJoin(['u' => $subQuery], 'u.id = author_id');

В этом случае вы должны поместить подзапрос в массив и использовать ключ массива для указания псевдонима.

union()

Метод union() задает фрагмент UNION SQL-запроса. Например,

$query1 = (new \yii\db\Query())
    ->select("id, category_id AS type, name")
    ->from('post')
    ->limit(10);

$query2 = (new \yii\db\Query())
    ->select('id, type, name')
    ->from('user')
    ->limit(10);

$query1->union($query2);

Вы можете вызывать union() несколько раз, чтобы добавить больше фрагментов UNION.

Методы запроса

yii\db\Query предоставляет целый набор методов для различных целей запросов:

  • all(): возвращает массив строк, где каждая строка является ассоциативным массивом пар имя-значение.
  • one(): возвращает первую строку результата.
  • column(): возвращает первый столбец результата.
  • scalar(): возвращает скалярное значение, расположенное в первой строке и первом столбце результата.
  • exists(): возвращает значение, указывающее, содержит ли запрос какой-либо результат.
  • count(): возвращает результат запроса COUNT.
  • Другие методы агрегированных запросов, включая sum($q), average($q), max($q), min($q). Параметр $q обязателен для этих методов и может быть именем столбца или выражением БД.

Например,

// SELECT `id`, `email` FROM `user`
$rows = (new \yii\db\Query())
    ->select(['id', 'email'])
    ->from('user')
    ->all();
    
// SELECT * FROM `user` WHERE `username` LIKE `%test%`
$row = (new \yii\db\Query())
    ->from('user')
    ->where(['like', 'username', 'test'])
    ->one();

Примечание: Метод one() возвращает только первую строку результата запроса. Он не добавляет LIMIT 1 к генерируемому SQL-запросу. Это нормально и предпочтительно, если вы знаете, что запрос вернёт только одну или несколько строк данных (например, если вы выполняете запрос с первичными ключами). Однако, если запрос может потенциально вернуть множество строк данных, вы должны явно вызвать limit(1) для повышения производительности, например, (new \yii\db\Query())->from('user')->limit(1)->one().

Все эти методы запросов принимают необязательный параметр $db, представляющий соединение БД, которое следует использовать для выполнения запроса к БД. Если вы опустите этот параметр, будет использовано db компонента приложения в качестве соединения БД. Ниже приведен ещё один пример использования метода запроса count():

// executes SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name
$count = (new \yii\db\Query())
    ->from('user')
    ->where(['last_name' => 'Smith'])
    ->count();

При вызове метода запроса yii\db\Query внутренне выполняется следующее:

  • Вызов yii\db\QueryBuilder для генерации SQL-запроса на основе текущей конструкции yii\db\Query;
  • Создание объекта yii\db\Command с сгенерированным SQL-запросом;
  • Вызов метода запроса (например, queryAll()) объекта yii\db\Command для выполнения SQL-запроса и извлечения данных.

Иногда вам может понадобиться изучить или использовать SQL-запрос, построенный из объекта yii\db\Query. Этого можно достичь с помощью следующего кода:

$command = (new \yii\db\Query())
    ->select(['id', 'email'])
    ->from('user')
    ->where(['last_name' => 'Smith'])
    ->limit(10)
    ->createCommand();
    
// show the SQL statement
echo $command->sql;
// show the parameters to be bound
print_r($command->params);

// returns all rows of the query result
$rows = $command->queryAll();

Индексирование результатов запроса

При вызове all() возвращается массив строк, индексированных последовательными целыми числами. Иногда может потребоваться индексирование по-другому, например, по значению определённого столбца или выражения. Это можно достичь, вызвав indexBy() перед all(). Например,

// returns [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]
$query = (new \yii\db\Query())
    ->from('user')
    ->limit(10)
    ->indexBy('id')
    ->all();

Для индексирования по значениям выражений передайте анонимную функцию методу indexBy():

$query = (new \yii\db\Query())
    ->from('user')
    ->indexBy(function ($row) {
        return $row['id'] . $row['username'];
    })->all();

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

Примечание: В отличие от методов запросов, таких как groupBy() или orderBy(), которые преобразуются в SQL и являются частью запроса, этот метод работает после извлечения данных из базы данных. Это означает, что могут использоваться только те имена столбцов, которые были частью SELECT в вашем запросе. Также, если вы выбрали столбец с префиксом таблицы, например customer.id, результат набора будет содержать только id, поэтому вы должны вызвать ->indexBy('id') без префикса таблицы.

Запрос по частям

При работе с большими объемами данных методы, такие как yii\db\Query::all(), не подходят, так как они требуют загрузки всего результата запроса в память клиента. Для решения этой проблемы Yii предоставляет поддержку запросов по частям. Результат запроса хранится на сервере, а клиент использует указатель для итерации по результату набора по частям.

Предупреждение: Существуют известные ограничения и обходные пути для реализации MySQL запросов по частям. См. ниже.

Запрос по частям можно использовать следующим образом:

use yii\db\Query;

$query = (new Query())
    ->from('user')
    ->orderBy('id');

foreach ($query->batch() as $users) {
    // $users is an array of 100 or fewer rows from the user table
}

// or to iterate the row one by one
foreach ($query->each() as $user) {
    // data is being fetched from the server in batches of 100,
    // but $user represents one row of data from the user table
}

Методы yii\db\Query::batch() и yii\db\Query::each() возвращают объект yii\db\BatchQueryResult, который реализует интерфейс Iterator и поэтому может быть использован в конструкции foreach. Во время первой итерации выполняется SQL-запрос к базе данных. Данные извлекаются по частям в оставшихся итерациях. По умолчанию размер части равен 100, что означает извлечение 100 строк данных в каждой части. Вы можете изменить размер части, передав первый параметр методам batch() или each().

По сравнению с yii\db\Query::all(), запрос по частям загружает в память только 100 строк данных за раз.

Если вы укажите индексирование результата запроса по столбцу с помощью yii\db\Query::indexBy(), запрос по частям всё равно сохранит правильный индекс.

Например:

$query = (new \yii\db\Query())
    ->from('user')
    ->indexBy('username');

foreach ($query->batch() as $users) {
    // $users is indexed by the "username" column
}

foreach ($query->each() as $username => $user) {
    // ...
}

Ограничения запроса по частям в MySQL

Реализация запросов по частям в MySQL зависит от библиотеки драйвера PDO. По умолчанию SQL-запросы MySQL являются buffered. Это подрывает цель использования курсора для получения данных, потому что не предотвращает загрузку всего набора результатов в память клиента драйвером.

Примечание: Когда libmysqlclient используется (типичный случай для PHP5), лимит памяти PHP не учитывает память, используемую для наборов результатов. Может показаться, что запросы по частям работают правильно, но на самом деле весь набор данных загружается в память клиента и может её исчерпать.

Для отключения буферизации и уменьшения потребностей в памяти клиента, свойство соединения PDO PDO::MYSQL_ATTR_USE_BUFFERED_QUERY должно быть установлено в значение false. Однако до тех пор, пока весь набор данных не будет извлечён, нельзя выполнить другой запрос через то же самое соединение. Это может помешать ActiveRecord выполнить запрос на получение схемы таблицы, когда она потребуется. Если это не проблема (схема таблицы уже кэширована), можно переключить исходное соединение в режим без буферизации, а затем откатить его, когда запрос по частям завершится.

Yii::$app->db->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

// Do batch query

Yii::$app->db->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);

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

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

$unbufferedDb = new \yii\db\Connection([
    'dsn' => Yii::$app->db->dsn,
    'username' => Yii::$app->db->username,
    'password' => Yii::$app->db->password,
    'charset' => Yii::$app->db->charset,
]);
$unbufferedDb->open();
$unbufferedDb->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

Если вы хотите убедиться, что $unbufferedDb имеет точно такие же атрибуты PDO, как исходное буферизованное соединение $db, но PDO::MYSQL_ATTR_USE_BUFFERED_QUERY является false, рассмотрите глубокую копию $db, установив её в значение false вручную.

Затем запросы создаются обычным образом. Новое соединение используется для выполнения запросов по частям и извлечения результатов либо по частям, либо по одному:

// getting data in batches of 1000
foreach ($query->batch(1000, $unbufferedDb) as $users) {
    // ...
}


// data is fetched from server in batches of 1000, but is iterated one by one 
foreach ($query->each(1000, $unbufferedDb) as $user) {
    // ...
}

Когда соединение больше не нужно и набор результатов был извлечён, его можно закрыть:

$unbufferedDb->close();

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

© 2008–2017 by Yii Software LLC
Licensed under the three clause BSD license.
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API