mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-03 22:32:40 +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)»](guide-db-dao.html#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`](http://www.postgresql.org/docs/8.3/static/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`.
 | 
						||
For example,
 | 
						||
 | 
						||
```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) {
 | 
						||
    // ...
 | 
						||
}
 | 
						||
```
 |