mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-10-31 10:39:59 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			681 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			681 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| Построитель запросов
 | ||
| ====================
 | ||
| 
 | ||
| Построенный поверх [DAO](db-dao.md), построитель запросов позволяет конструировать SQL выражения в программируемом и
 | ||
| независимом от СУБД виде. В сравнении с написанием чистого SQL выражения, использование построителя помогает 
 | ||
| вам писать более читаемый связанный с SQL код и генерировать более безопасные SQL выражения.
 | ||
| 
 | ||
| Использование построителя запросов, как правило, включает два этапа:
 | ||
| 
 | ||
| 1. Создание объекта [[yii\db\Query]] представляющего различные части (такие как `SELECT`, `FROM`) SQL выражения SELECT.
 | ||
| 2. Выполнить запрос методом [[yii\db\Query]] (таким как `all()`) для извлечения данных из базы данных.
 | ||
| 
 | ||
| Следующий код показывает обычное использование построителя запросов:
 | ||
| 
 | ||
| ```php
 | ||
| $rows = (new \yii\db\Query())
 | ||
|     ->select(['id', 'email'])
 | ||
|     ->from('user')
 | ||
|     ->where(['last_name' => 'Smith'])
 | ||
|     ->limit(10)
 | ||
|     ->all();
 | ||
| ```
 | ||
| 
 | ||
| Приведённый выше код создаёт и выполняет следующее SQL выражение, где параметр `:last_name` привязывается к строке `'Smith'`.
 | ||
| 
 | ||
| ```sql
 | ||
| SELECT `id`, `email` 
 | ||
| FROM `user`
 | ||
| WHERE `last_name` = :last_name
 | ||
| LIMIT 10
 | ||
| ```
 | ||
| 
 | ||
| > Info: В основном вы будете работать с [[yii\db\Query]] вместо [[yii\db\QueryBuilder]]. Последний вызывается
 | ||
|   неявно при вызове одного из методов запроса. [[yii\db\QueryBuilder]] это класс, отвечающий за генерацию зависимого
 | ||
|   от СУБД SQL выражения (такие как экранирование имён таблиц/столбцов) из независимых от СУБД объектов [[yii\db\Query]]. 
 | ||
| 
 | ||
| 
 | ||
| ## Построение запросов <span id="building-queries"></span>
 | ||
| 
 | ||
| Создав объект [[yii\db\Query]], вы можете вызвать различные методы для создания различных частей SQL выражения.
 | ||
| Имена методов напоминают ключевые слова SQL, используемые в соответствующих частях SQL запроса. Например,
 | ||
| чтобы указать `FROM` часть запроса, вам нужно вызвать метод [[yii\db\Query::from()|from()]]. Все методы построителя
 | ||
| запросов возвращают свой объект, который позволяет объединять несколько вызовов в цепочку.
 | ||
| 
 | ||
| Далее будет описание каждого метода построителя запросов.
 | ||
| 
 | ||
| ### [[yii\db\Query::select()|select()]] <span id="select"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::select()|select()]] определяет фрагмент `SELECT` SQL запроса. Вы можете указать столбцы, которые
 | ||
| должны быть выбраны, они должны быть указаны в виде массива или строки. Имена столбцов автоматически экранируются
 | ||
| при создании SQL-запроса при его генерации из объекта [[yii\db\Query]].
 | ||
|  
 | ||
| ```php
 | ||
| $query->select(['id', 'email']);
 | ||
| 
 | ||
| // эквивалентно:
 | ||
| 
 | ||
| $query->select('id, email');
 | ||
| ```
 | ||
| 
 | ||
| Имена столбцов могут быть выбраны вместе с префиксами таблиц и/или алиасами столбцов, также как при записи обычного SQL выражения.
 | ||
| Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->select(['user.id AS user_id', 'email']);
 | ||
| 
 | ||
| // эквивалентно:
 | ||
| 
 | ||
| $query->select('user.id AS user_id, email');
 | ||
| ```
 | ||
| 
 | ||
| Если вы используете формат массива для указания столбцов, вы можете также указать ключи массива для указания алиасов столбцов.
 | ||
| Например, приведённый выше код может быть переписан:
 | ||
| 
 | ||
| ```php
 | ||
| $query->select(['user_id' => 'user.id', 'email']);
 | ||
| ```
 | ||
| 
 | ||
| Если вы не вызываете метод [[yii\db\Query::select()|select()]] при создании запроса, будет использована `*`, что означает
 | ||
| выбрать *все* столбцы.
 | ||
| 
 | ||
| Кроме имён столбцов, вы можете также использовать SQL выражения. Вы должны использовать формат массива для использования
 | ||
| выражений, которые содержат запятые для предотвращения некорректного автоматического экранирования. Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->select(["CONCAT(first_name, ' ', last_name) AS full_name", 'email']); 
 | ||
| ```
 | ||
| 
 | ||
| Начиная с версии 2.0.1, вы также можете использовать подзапросы. Вы должны указывать каждый подзапрос в выражении как
 | ||
| объект [[yii\db\Query]]. Например,
 | ||
|  
 | ||
| ```php
 | ||
| $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');
 | ||
| ```
 | ||
| 
 | ||
| Чтоб выбрать конкретные строки, вы можете вызвать метод [[yii\db\Query::distinct()|distinct()]]:
 | ||
| 
 | ||
| ```php
 | ||
| // SELECT DISTINCT `user_id` ...
 | ||
| $query->select('user_id')->distinct();
 | ||
| ```
 | ||
| 
 | ||
| Вы можете вызвать [[yii\db\Query::addSelect()|addSelect()]] для добавления полей. Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->select(['id', 'username'])
 | ||
|     ->addSelect(['email']);
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| ### [[yii\db\Query::from()|from()]] <span id="from"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::from()|from()]] указывает фрагмент `FROM` SQL запроса. Например,
 | ||
| 
 | ||
| ```php
 | ||
| // SELECT * FROM `user`
 | ||
| $query->from('user');
 | ||
| ```
 | ||
| 
 | ||
| Вы можете указать имена таблиц в виде строки или массива. Имена таблиц могут содержать префикс схемы и/или алиасы
 | ||
| таблиц, как при написании обычного SQL выражения. Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->from(['public.user u', 'public.post p']);
 | ||
| 
 | ||
| // эквивалентно:
 | ||
| 
 | ||
| $query->from('public.user u, public.post p');
 | ||
| ```
 | ||
| 
 | ||
| Если вы используете формат массива, вы можете использовать ключи массива для указания алиасов:
 | ||
| 
 | ||
| ```php
 | ||
| $query->from(['u' => 'public.user', 'p' => 'public.post']);
 | ||
| ```
 | ||
| 
 | ||
| Кроме имён таблиц, вы можете так же, как и в select, указывать подзапросы в виде объекта [[yii\db\Query]].
 | ||
| 
 | ||
| ```php
 | ||
| $subQuery = (new Query())->select('id')->from('user')->where('status=1');
 | ||
| 
 | ||
| // SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u 
 | ||
| $query->from(['u' => $subQuery]);
 | ||
| ```
 | ||
| 
 | ||
| #### Префиксы
 | ||
| 
 | ||
| Также может применяться [[yii\db\Connection::$tablePrefix|tablePrefix]] по умолчанию. Подробное описание смотрите
 | ||
| в подразделе [«Экранирование имён таблиц и столбцов» раздела «Объекты доступа к данным (DAO)»](db-dao.md#quoting-table-and-column-names).
 | ||
| 
 | ||
| ### [[yii\db\Query::where()|where()]] <span id="where"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::where()|where()]] определяет фрагмент `WHERE` SQL выражения. Вы можете использовать один из
 | ||
| трёх форматов:
 | ||
| 
 | ||
| - строковый формат, Например, `'status=1'`
 | ||
| - формат массива, Например, `['status' => 1, 'type' => 2]`
 | ||
| - формат операторов, Например, `['like', 'name', 'test']`
 | ||
| 
 | ||
| 
 | ||
| #### Строковый формат <span id="string-format"></span>
 | ||
| 
 | ||
| Строковый формат - это лучший выбор для простых условий. Он работает так, будто вы просто пишете SQL запрос. Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->where('status=1');
 | ||
| 
 | ||
| // или используя привязку параметров
 | ||
| $query->where('status=:status', [':status' => $status]);
 | ||
| ```
 | ||
| 
 | ||
| Не встраивайте переменные непосредственно в условие, особенно если значение переменной получено от пользователя,
 | ||
| потому что это делает ваше приложение подверженным атакам через SQL инъекции.
 | ||
|  
 | ||
| ```php
 | ||
| // Опасность! Не делайте так если вы не уверены, что $status это действительно число.
 | ||
| $query->where("status=$status");
 | ||
| ```
 | ||
| 
 | ||
| При использовании привязки параметров, вы можете вызывать [[yii\db\Query::params()|params()]] или
 | ||
| [[yii\db\Query::addParams()|addParams()]] для раздельного указания параметров.
 | ||
| 
 | ||
| ```php
 | ||
| $query->where('status=:status')
 | ||
|     ->addParams([':status' => $status]);
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| #### Формат массива <span id="hash-format"></span>
 | ||
| 
 | ||
| Формат массива лучше всего использовать для указания нескольких объединяемых через `AND` условий, каждое из которых
 | ||
| является простым равенством. Он описывается в виде массива, ключами которого являются имена столбцов, а значения
 | ||
| соответствуют значениям столбцов.
 | ||
| 
 | ||
| ```php
 | ||
| // ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))
 | ||
| $query->where([
 | ||
|     'status' => 10,
 | ||
|     'type' => null,
 | ||
|     'id' => [4, 8, 15],
 | ||
| ]);
 | ||
| ```
 | ||
| 
 | ||
| Как вы можете видеть, построитель запросов достаточно умен, чтобы правильно обрабатывать значения `null` или массивов.
 | ||
| 
 | ||
| Вы также можете использовать подзапросы:
 | ||
| 
 | ||
| ```php
 | ||
| $userQuery = (new Query())->select('id')->from('user');
 | ||
| 
 | ||
| // ...WHERE `id` IN (SELECT `id` FROM `user`)
 | ||
| $query->where(['id' => $userQuery]);
 | ||
| ```
 | ||
| 
 | ||
| Используя формат массива, Yii автоматически привязывает значения массива как параметры, потому в отличие от [строкового формата](#string-format),
 | ||
| привязывать параметры вручную не требуется. Обратите внимание, что Yii никогда НЕ экранирует имена столбцов (ключи массива),
 | ||
| потому если вы используете как ключ массива переменную, полученную от пользователя без дополнительной проверки, ваше приложение
 | ||
| становится подверженным атаке через SQL инъекцию. Чтобы избежать этого, используйте для имён столбцов только проверененные данные,
 | ||
| предпочитая фильтрацию по «белому списку». Если вам нужно использовать имя столбца, полученное от пользователя,
 | ||
| вам может быть полезным раздел [Фильтрация данных](output-data-widgets.md#filtering-data). Ниже приведён пример уязвимого кода:
 | ||
| 
 | ||
| ```php
 | ||
| // Уязвимый код:
 | ||
| $column = $request->get('column');
 | ||
| $value = $request->get('value');
 | ||
| $query->where([$column => $value]);
 | ||
| // $value будет безопасно привязано как параметр, но $column – нет!
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| #### Формат операторов <span id="operator-format"></span>
 | ||
| 
 | ||
| Формат оператора позволяет задавать произвольные условия в программном стиле. Он имеет следующий вид:
 | ||
| 
 | ||
| ```php
 | ||
| [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`: первый операнд должен быть именем столбца, а второй и третий оператор должны быть начальным и конечным
 | ||
|    значением диапазона. Например, `['between', 'id', 1, 10]` сгенерирует `id BETWEEN 1 AND 10`.
 | ||
| 
 | ||
| - `not between`: похож на `between` за исключением того, что `BETWEEN` заменяется на `NOT BETWEEN`
 | ||
|   в сгенерированном условии.
 | ||
| 
 | ||
| - `in`: первый операнд должен быть столбцом или выражением БД. Второй операнд может быть либо массивом, либо объектом `Query`.
 | ||
|   Будет сгенерировано условие `IN`. Если второй операнд массив, он будет представлять набор значений, которым может быть
 | ||
|   равен столбец или выражение БД; Если второй операнд объект `Query`, будет сформирован подзапрос, который будет использован
 | ||
|   как диапазон для столбца или выражения БД. Например, `['in', 'id', [1, 2, 3]]` сформирует `id IN (1, 2, 3)`.
 | ||
|   Метод будет правильно экранировать имя столбца и значения диапазона. Оператор `in` также поддерживает составные столбцы.
 | ||
|   В этом случае, первый операнд должен быть массивом названий столбцов, в то время как операнд 2 должен быть массивом
 | ||
|   массивов или объектом `Query` представляющим диапазоны для столбцов.
 | ||
| 
 | ||
| - `not in`: похож на оператор `in`, кроме того что `IN` будет заменён на `NOT IN` в сформированном условии.
 | ||
| 
 | ||
| - `like`: первый операнд должен быть столбцом или выражением БД, а второй операнд будет строкой или массивом представляющим
 | ||
|   значения, на которые должны быть похожи столбцы или выражения БД. Например, `['like', 'name', 'tester']`
 | ||
|   сформирует `name LIKE '%tester%'`. 
 | ||
|   Когда диапазон значений задан в виде массива, несколько `LIKE` утверждений будут сформированы и соединены с помощью `AND`.
 | ||
|   Например, `['like', 'name', ['test', 'sample']]` сформирует `name LIKE '%test%' AND name LIKE '%sample%'`.
 | ||
|   Вы также можете передать третий необязательный операнд, для указания способа экранирования специальных символов в значениях.
 | ||
|   Операнд должен быть представлен массивом соответствия специальных символов их экранированным аналогам. Если этот
 | ||
|   операнд не задан, то будет использовано соответствие по умолчанию. Вы можете также использовать значение `false`
 | ||
|   или пустой массив, чтоб указать что значения уже экранированы. Обратите внимание, что при использовании массива соответствия
 | ||
|   экранирования (или если третий операнд не передан), значения будут автоматически заключены в символы процентов.
 | ||
| 
 | ||
|   > Note: При использовании PostgreSQL вы можете использовать также [`ilike`](https://www.postgresql.org/docs/8.3/functions-matching.html#FUNCTIONS-LIKE)
 | ||
|   > вместо `like` для регистронезависимого поиска.
 | ||
| 
 | ||
| - `or like`: похож на оператор `like`, только утверждения `LIKE` будут объединяться с помощью оператора `OR`, если
 | ||
|   второй операнд будет представлен массивом.
 | ||
| 
 | ||
| - `not like`: похож на оператор `like`, только `LIKE` будет заменён на `NOT LIKE` в сгенерированном условии.
 | ||
| 
 | ||
| - `or not like`: похож на оператор `not like`, только утверждения `NOT LIKE` будут объединены с помощью `OR`.
 | ||
| 
 | ||
| - `exists`: требует один операнд, который должен быть экземпляром [[yii\db\Query]] представляющим подзапрос. Будет
 | ||
|   сгенерировано выражение `EXISTS (sub-query)`.
 | ||
| 
 | ||
| - `not exists`: похож на оператор `exists` и сформирует выражение `NOT EXISTS (sub-query)`.
 | ||
| 
 | ||
| - `>`, `<=`, или другие валидные операторы БД, которые требуют двух операндов: первый операнд должен быть именем
 | ||
|   столбца, второй операнд это значение. Например, `['>', 'age', 10]` сформирует `age>10`.
 | ||
| 
 | ||
| Используя формат операторов, Yii автоматически привязывает значения для сравнения как параметры, потому в отличие от [строкового формата](#string-format),
 | ||
| привязывать параметры вручную не требуется. Обратите внимание, что Yii никогда НЕ экранирует имена столбцов,
 | ||
| потому если вы используете как имя столбца переменную, полученную от пользователя без дополнительной проверки, ваше приложение
 | ||
| становится подверженным атаке через SQL инъекцию. Чтобы избежать этого, используйте для имён столбцов только проверененные данные,
 | ||
| предпочитая фильтрацию по «белому списку». Если вам нужно использовать имя столбца, полученное от пользователя,
 | ||
| вам может быть полезным раздел [Фильтрация данных](output-data-widgets.md#filtering-data). Ниже приведён пример уязвимого кода:
 | ||
| 
 | ||
| ```php
 | ||
| // Уязвимый код:
 | ||
| $column = $request->get('column');
 | ||
| $value = $request->get('value');
 | ||
| $query->where(['=', $column, $value]);
 | ||
| // $value будет безопасно привязано как параметр, но $column – нет!
 | ||
| ```
 | ||
| 
 | ||
| #### Добавление условий <span id="appending-conditions"></span>
 | ||
| 
 | ||
| Вы можете использовать [[yii\db\Query::andWhere()|andWhere()]] или [[yii\db\Query::orWhere()|orWhere()]] для добавления
 | ||
| дополнительных условий. Вы можете использовать эти вызовы несколько раз для добавления нескольких условий.
 | ||
| Например,
 | ||
| 
 | ||
| ```php
 | ||
| $status = 10;
 | ||
| $search = 'yii';
 | ||
| 
 | ||
| $query->where(['status' => $status]);
 | ||
| 
 | ||
| if (!empty($search)) {
 | ||
|     $query->andWhere(['like', 'title', $search]);
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| Если `$search` не пустое, то будет сформировано следующее условие WHERE:
 | ||
| 
 | ||
| ```sql
 | ||
| WHERE (`status` = 10) AND (`title` LIKE '%yii%')
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| #### Условия для фильтров <span id="filter-conditions"></span>
 | ||
| 
 | ||
| Когда условие `WHERE` формируется на основе пользовательского ввода, обычно, хочется проигнорировать не заданные значения.
 | ||
| Например, в форме поиска, которая позволяет осуществлять поиск по имени пользователя или email, вы хотели бы игнорировать
 | ||
| username/email условие, если пользователь ничего не ввёл в поле ввода. Вы можете достичь этого используя метод
 | ||
| [[yii\db\Query::filterWhere()|filterWhere()]].
 | ||
| 
 | ||
| ```php
 | ||
| // $username и $email вводит пользователь
 | ||
| $query->filterWhere([
 | ||
|     'username' => $username,
 | ||
|     'email' => $email,
 | ||
| ]);
 | ||
| ```
 | ||
| 
 | ||
| Единственное отличие между [[yii\db\Query::filterWhere()|filterWhere()]] и [[yii\db\Query::where()|where()]] 
 | ||
| заключается в игнорировании пустых значений, переданных в условие в [форме массива](#hash-format). Таким образом
 | ||
| если `$email` будет пустым, а `$username` нет, то приведённый выше код сформирует условие `WHERE username=:username`.
 | ||
| 
 | ||
| > Info: значение признаётся пустым, если это `null`, пустой массив, пустая строка или строка состоящая из одних пробельных символов.
 | ||
| 
 | ||
| Также вместо [[yii\db\Query::andWhere()|andWhere()]] и [[yii\db\Query::orWhere()|orWhere()]], вы можете использовать
 | ||
| [[yii\db\Query::andFilterWhere()|andFilterWhere()]] и [[yii\db\Query::orFilterWhere()|orFilterWhere()]]
 | ||
| для добавления дополнительных условий фильтрации.
 | ||
| 
 | ||
| 
 | ||
| ### [[yii\db\Query::orderBy()|orderBy()]] <span id="order-by"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::orderBy()|orderBy()]] определяет фрагмент `ORDER BY` SQL выражения. Например,
 | ||
| 
 | ||
| ```php
 | ||
| // ... ORDER BY `id` ASC, `name` DESC
 | ||
| $query->orderBy([
 | ||
|     'id' => SORT_ASC,
 | ||
|     'name' => SORT_DESC,
 | ||
| ]);
 | ||
| ```
 | ||
| 
 | ||
| В данном коде, ключи массива - это имена столбцов, а значения массива - это соответствующее направление сортировки.
 | ||
| PHP константа `SORT_ASC` определяет сортировку по возрастанию и `SORT_DESC` сортировку по убыванию.
 | ||
| 
 | ||
| Если `ORDER BY` содержит только простые имена столбцов, вы можете определить их с помощью столбцов, также
 | ||
| как и при написании обычного SQL. Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->orderBy('id ASC, name DESC');
 | ||
| ```
 | ||
| 
 | ||
| > Note: Вы должны использовать массив для указания `ORDER BY` содержащих выражения БД.
 | ||
| 
 | ||
| Вы можете вызывать [[yii\db\Query::addOrderBy()|addOrderBy()]] для добавления столбцов во фрагмент `ORDER BY`.
 | ||
| 
 | ||
| ```php
 | ||
| $query->orderBy('id ASC')
 | ||
|     ->addOrderBy('name DESC');
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| ### [[yii\db\Query::groupBy()|groupBy()]] <span id="group-by"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::groupBy()|groupBy()]] определяет фрагмент `GROUP BY` SQL запроса.
 | ||
| 
 | ||
| ```php
 | ||
| // ... GROUP BY `id`, `status`
 | ||
| $query->groupBy(['id', 'status']);
 | ||
| ```
 | ||
| 
 | ||
| Если фрагмент `GROUP BY` содержит только простые имена столбцов, вы можете указать их используя строку, также как в
 | ||
| обычном SQL выражении.
 | ||
| 
 | ||
| ```php
 | ||
| $query->groupBy('id, status');
 | ||
| ```
 | ||
| 
 | ||
| > Note: Вы должны использовать массив для указания `GROUP BY` содержащих выражения БД.
 | ||
|  
 | ||
| Вы можете вызывать [[yii\db\Query::addGroupBy()|addGroupBy()]] для добавления имён столбцов во фрагмент `GROUP BY`.
 | ||
| Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->groupBy(['id', 'status'])
 | ||
|     ->addGroupBy('age');
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| ### [[yii\db\Query::having()|having()]] <span id="having"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::having()|having()]] определяет фрагмент `HAVING` SQL запроса. Он принимает условия, которое
 | ||
| может быть определено тем же способом, что и для [where()](#where).
 | ||
| 
 | ||
| ```php
 | ||
| // ... HAVING `status` = 1
 | ||
| $query->having(['status' => 1]);
 | ||
| ```
 | ||
| 
 | ||
| Пожалуйста, обратитесь к документации для [where()](#where) для более подробной информации об определении условий.
 | ||
| 
 | ||
| Вы можете вызывать [[yii\db\Query::andHaving()|andHaving()]] или [[yii\db\Query::orHaving()|orHaving()]] для добавления
 | ||
| дополнительных условий во фрагмент `HAVING`.
 | ||
| 
 | ||
| ```php
 | ||
| // ... HAVING (`status` = 1) AND (`age` > 30)
 | ||
| $query->having(['status' => 1])
 | ||
|     ->andHaving(['>', 'age', 30]);
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| ### [[yii\db\Query::limit()|limit()]] и [[yii\db\Query::offset()|offset()]] <span id="limit-offset"></span>
 | ||
| 
 | ||
| Методы [[yii\db\Query::limit()|limit()]] и [[yii\db\Query::offset()|offset()]] определяют фрагменты `LIMIT`
 | ||
| и `OFFSET` SQL запроса.
 | ||
|  
 | ||
| ```php
 | ||
| // ... LIMIT 10 OFFSET 20
 | ||
| $query->limit(10)->offset(20);
 | ||
| ```
 | ||
| 
 | ||
| Если вы определяете неправильный limit или offset (например отрицательное значение), они будут проигнорированы.
 | ||
| 
 | ||
| > Info: Для СУБД, которые не поддерживают `LIMIT` и `OFFSET` (такие как MSSQL), построитель запросов будет
 | ||
|   генерировать SQL выражения, которые эмулирует поведение `LIMIT`/`OFFSET`.
 | ||
| 
 | ||
| 
 | ||
| ### [[yii\db\Query::join()|join()]] <span id="join"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::join()|join()]] определяет фрагмент `JOIN` SQL запроса.
 | ||
| 
 | ||
| ```php
 | ||
| // ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`
 | ||
| $query->join('LEFT JOIN', 'post', 'post.user_id = user.id');
 | ||
| ```
 | ||
| 
 | ||
| Метод [[yii\db\Query::join()|join()]] принимает четыре параметра:
 | ||
| 
 | ||
| - `$type`: тип объединения, например, `'INNER JOIN'`, `'LEFT JOIN'`.
 | ||
| - `$table`: имя таблицы, которая должна быть присоединена.
 | ||
| - `$on`: необязательное условие объединения, то есть фрагмент `ON`. Пожалуйста, обратитесь к документации для
 | ||
|   [where()](#where) для более подробной информации об определении условий. Отметим, что синтаксис массивов **не работает**
 | ||
|   для задания условий для столбцов, то есть `['user.id' => 'comment.userId']` будет означать условие, где ID пользователя
 | ||
|   должен быть равен строке `'comment.userId'`. Вместо этого стоит указывать условие в виде строки `'user.id = comment.userId'`.
 | ||
| - `$params`: необязательные параметры присоединяемые к условию объединения.
 | ||
| 
 | ||
| Вы можете использовать следующие сокращающие методы для указания `INNER JOIN`, `LEFT JOIN` и `RIGHT JOIN`, в указанном порядке.
 | ||
| 
 | ||
| - [[yii\db\Query::innerJoin()|innerJoin()]]
 | ||
| - [[yii\db\Query::leftJoin()|leftJoin()]]
 | ||
| - [[yii\db\Query::rightJoin()|rightJoin()]]
 | ||
| 
 | ||
| Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query->leftJoin('post', 'post.user_id = user.id');
 | ||
| ```
 | ||
| 
 | ||
| Для соединения с несколькими таблицами, вызовите вышеуказанные методы несколько раз.
 | ||
| 
 | ||
| Кроме соединения с таблицами, вы можете также присоединять подзапросы. Чтобы это сделать, укажите объединяемый подзапрос
 | ||
| как объект [[yii\db\Query]].
 | ||
| 
 | ||
| ```php
 | ||
| $subQuery = (new \yii\db\Query())->from('post');
 | ||
| $query->leftJoin(['u' => $subQuery], 'u.id = author_id');
 | ||
| ```
 | ||
| 
 | ||
| В этом случае вы должны передать подзапросы в массиве и использовать ключи для определения алиасов.
 | ||
| 
 | ||
| 
 | ||
| ### [[yii\db\Query::union()|union()]] <span id="union"></span>
 | ||
| 
 | ||
| Метод [[yii\db\Query::union()|union()]] определяет фрагмент `UNION` SQL запроса.
 | ||
| 
 | ||
| ```php
 | ||
| $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);
 | ||
| ```
 | ||
| 
 | ||
| Вы можете вызвать [[yii\db\Query::union()|union()]] несколько раз для присоединения фрагментов `UNION`. 
 | ||
| 
 | ||
| 
 | ||
| ## Методы выборки <span id="query-methods"></span>
 | ||
| 
 | ||
| [[yii\db\Query]] предоставляет целый набор методов для разных вариантов выборки:
 | ||
| 
 | ||
| - [[yii\db\Query::all()|all()]]: возвращает массив строк, каждая из которых это ассоциативный массив пар ключ-значение.
 | ||
| - [[yii\db\Query::one()|one()]]: возвращает первую строку запроса.
 | ||
| - [[yii\db\Query::column()|column()]]: возвращает первый столбец результата.
 | ||
| - [[yii\db\Query::scalar()|scalar()]]: возвращает скалярное значение первого столбца первой строки результата.
 | ||
| - [[yii\db\Query::exists()|exists()]]: возвращает значение указывающее, что выборка содержит результат.
 | ||
| - [[yii\db\Query::count()|count()]]: возвращает результат `COUNT` запроса.
 | ||
| - Другие методы агрегирования запросов, включая [[yii\db\Query::sum()|sum($q)]], [[yii\db\Query::average()|average($q)]],
 | ||
|   [[yii\db\Query::max()|max($q)]], [[yii\db\Query::min()|min($q)]]. Параметр `$q` обязателен для этих методов и могут
 | ||
|   содержать либо имя столбца, либо выражение БД.
 | ||
| 
 | ||
| Например,
 | ||
| 
 | ||
| ```php
 | ||
| // 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();
 | ||
| ```
 | ||
| 
 | ||
| > Note: метод [[yii\db\Query::one()|one()]] вернёт только первую строку результата запроса. Он НЕ добавляет
 | ||
|   `LIMIT 1` в генерируемый SQL. Это хорошо и предпочтительно если вы знаете, что запрос вернёт только одну или несколько
 | ||
|   строк данных (например, при запросе по первичному ключу). Однако если запрос потенциально может вернут много
 | ||
|   строк данных, вы должны вызвать `limit(1)` для повышения производительности, Например, 
 | ||
|   `(new \yii\db\Query())->from('user')->limit(1)->one()`.
 | ||
| 
 | ||
| Все методы выборки могут получать необязательный параметр `$db`, представляющий [[yii\db\Connection|соединение с БД]],
 | ||
| которое должно использоваться, чтобы выполнить запрос к БД. Если вы упускаете этот параметр, будет использоваться
 | ||
| [компонент приложения](structure-application-components.md) `$db`. Ниже приведён ещё один пример использования метода
 | ||
| [[yii\db\Query::count()|count()]]:
 | ||
| 
 | ||
| ```php
 | ||
| // 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 запросом;
 | ||
| * Вызывается выбирающий метод (например [[yii\db\Command::queryAll()|queryAll()]]) из [[yii\db\Command]] для выполнения SQL запроса и извлечения данных.
 | ||
| 
 | ||
| Иногда вы можете захотеть увидеть или использовать SQL запрос построенный из объекта [[yii\db\Query]]. Этой цели можно
 | ||
| добиться с помощью следующего кода: 
 | ||
| 
 | ||
| ```php
 | ||
| $command = (new \yii\db\Query())
 | ||
|     ->select(['id', 'email'])
 | ||
|     ->from('user')
 | ||
|     ->where(['last_name' => 'Smith'])
 | ||
|     ->limit(10)
 | ||
|     ->createCommand();
 | ||
|     
 | ||
| // показать SQL запрос
 | ||
| echo $command->sql;
 | ||
| // показать привязываемые параметры
 | ||
| print_r($command->params);
 | ||
| 
 | ||
| // возвращает все строки запроса
 | ||
| $rows = $command->queryAll();
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| ### Индексация результатов запроса <span id="indexing-query-results"></span>
 | ||
| 
 | ||
| При вызове [[yii\db\Query::all()|all()]] возвращается массив строк индексированный последовательными целыми числами.
 | ||
| Иногда вам может потребоваться индексировать его по-другому, например, сделать индекс по указанному столбцу или
 | ||
| значением выражения. Вы можете реализовать такое поведение через вызов [[yii\db\Query::indexBy()|indexBy()]] перед
 | ||
| вызовом [[yii\db\Query::all()|all()]].
 | ||
| 
 | ||
| ```php
 | ||
| // возвращает [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]
 | ||
| $query = (new \yii\db\Query())
 | ||
|     ->from('user')
 | ||
|     ->limit(10)
 | ||
|     ->indexBy('id')
 | ||
|     ->all();
 | ||
| ```
 | ||
| 
 | ||
| Для индексации по значению выражения, передайте анонимную функцию в метод [[yii\db\Query::indexBy()|indexBy()]]:
 | ||
| 
 | ||
| ```php
 | ||
| $query = (new \yii\db\Query())
 | ||
|     ->from('user')
 | ||
|     ->indexBy(function ($row) {
 | ||
|         return $row['id'] . $row['username'];
 | ||
|     })->all();
 | ||
| ```
 | ||
| 
 | ||
| Анонимная функция должна принимать параметр `$row`, который содержит текущую строку запроса и должна вернуть скалярное
 | ||
| значение, которое будет использоваться как значение индекса для текущей строки.
 | ||
| 
 | ||
| 
 | ||
| ### Пакетная выборка <span id="batch-query"></span>
 | ||
| 
 | ||
| При работе с большими объемами данных, методы наподобие [[yii\db\Query::all()]] не подходят, потому что они требуют
 | ||
| загрузки всех данных в память. Чтобы сохранить требования к памяти минимальными, Yii предоставляет поддержку
 | ||
| так называемых пакетных выборок. Пакетная выборка делает возможным курсоры данных и выборку данных пакетами.
 | ||
| 
 | ||
| Пакетная выборка может использоваться следующим образом:
 | ||
| 
 | ||
| ```php
 | ||
| use yii\db\Query;
 | ||
| 
 | ||
| $query = (new Query())
 | ||
|     ->from('user')
 | ||
|     ->orderBy('id');
 | ||
| 
 | ||
| foreach ($query->batch() as $users) {
 | ||
|     // $users это массив из 100 или менее строк из таблицы пользователей
 | ||
| }
 | ||
| 
 | ||
| // или если вы хотите перебрать все строки по одной
 | ||
| foreach ($query->each() as $user) {
 | ||
|     // $user представляет одну строку из выборки
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| Метод [[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()]], в пакетной выборке индекс будет сохраняться.
 | ||
| Например,
 | ||
| 
 | ||
| ```php
 | ||
| $query = (new \yii\db\Query())
 | ||
|     ->from('user')
 | ||
|     ->indexBy('username');
 | ||
| 
 | ||
| foreach ($query->batch() as $users) {
 | ||
|     // $users индексируется по столбцу "username"
 | ||
| }
 | ||
| 
 | ||
| foreach ($query->each() as $username => $user) {
 | ||
|     // ...
 | ||
| }
 | ||
| ```
 | 
