miramir
9 years ago
2 changed files with 645 additions and 1 deletions
@ -0,0 +1,644 @@
|
||||
Построитель запросов |
||||
==================== |
||||
|
||||
Построеный поверх [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 |
||||
``` |
||||
|
||||
> Информация: В основном вы будете работать с [[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` часть запроса, вам нужно вызвать метод `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\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]); |
||||
``` |
||||
|
||||
|
||||
#### Формат операторов <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` |
||||
или пустой массив, чтоб указать что значения уже экранированы. Обратите внимание, что при использовании массива соответствия |
||||
экранирования (или если третий операнд не передан), значения будут автоматически заключены в символы процентов. |
||||
|
||||
> Примечание: При использовании 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`. |
||||
|
||||
|
||||
#### Добавление условий <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` не пустое, то будет сформировано следующее выражение: |
||||
|
||||
```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`. |
||||
|
||||
> Информация: значение признаётся пустым, если это 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'); |
||||
``` |
||||
|
||||
> Примечание: Вы должны использовать массив для указания `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'); |
||||
``` |
||||
|
||||
> Примечание: Вы должны использовать массив для указания `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 (например отрицательное значение), они будут проигнорированы. |
||||
|
||||
> Информация: Для СУБД, которые не поддерживают `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) для более подробной информации о определении условий. |
||||
- `$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(); |
||||
``` |
||||
|
||||
> Примечание: метод [[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`. Ниже приведён ещё один пример использования метода |
||||
`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 запросом; |
||||
* Вызывается выбирающий метод (например `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) { |
||||
} |
||||
``` |
Loading…
Reference in new issue