Query Builder (Construtor de Consulta) ============= Desenvolvido à partir do [Database Access Objects](db-dao.md), o query builder permite que você construa uma instrução SQL em um programático e independente de banco de dados. Comparado a escrever instruções SQL à mão, usar query builder lhe ajudará a escrever um código SQL relacional mais legível e gerar declarações SQL mais seguras. Usar query builder geralmente envolve dois passos: 1. Criar um objeto [[yii\db\Query]] para representar diferentes partes de uma instrução SQL (ex. `SELECT`, `FROM`). 2. Executar um método (ex. `all()`) do objeto [[yii\db\Query]] para recuperar dados do banco de dados. O código a seguir mostra uma forma habitual de utilizar query builder: ```php $rows = (new \yii\db\Query()) ->select(['id', 'email']) ->from('user') ->where(['last_name' => 'Smith']) ->limit(10) ->all(); ``` O código acima gera e executa a seguinte instrução SQL, onde o parâmetro `:last_name` está ligado a string `'Smith'`. ```sql SELECT `id`, `email` FROM `user` WHERE `last_name` = :last_name LIMIT 10 ``` > Observação: Geralmente, você trabalhará mais com o [[yii\db\Query]] do que com o [[yii\db\QueryBuilder]]. Este último é chamado pelo primeiro implicitamente quando você chama um dos métodos da query. O [[yii\db\QueryBuilder]] é a classe responsável por gerar instruções SGDBs dependentes (ex. colocar aspas em nomes de tabela/coluna) a partir de objetos de query independentemente do SGDB. ## Construindo Queries Para construir um objeto [[yii\db\Query]], você pode chamar diferentes métodos de construção de query para especificar diferentes partes de uma instrução SQL. Os nomes destes métodos assemelha-se as palavras-chave SQL utilizados nas partes correspondentes da instrução SQL. Por exemplo, para especificar a parte da instrução SQL `FROM`, você deve chamar o método `from()`. Todos os métodos de construção de query retornam o próprio objeto query, que permite você encadear várias chamadas em conjunto. A seguir, descreveremos o uso de cada método de construção de query. ### [[yii\db\Query::select()|select()]] O método [[yii\db\Query::select()|select()]] especifica o fragmento de uma instrução SQL `SELECT`. Você pode especificar colunas para ser selecionado em um array ou uma string, como mostrado abaixo. Os nomes das colunas que estão sendo selecionadas serão automaticamente envolvidas entre aspas quando a instrução SQL está sendo gerada a partir do objeto query. ```php $query->select(['id', 'email']); // equivalente a: $query->select('id, email'); ``` Os nomes das colunas que estão sendo selecionadas podem incluir prefixos de tabela e/ou aliases de colunas, como você faz ao escrever instruções SQL manualmente. Por exemplo: ```php $query->select(['user.id AS user_id', 'email']); // é equivalente a: $query->select('user.id AS user_id, email'); ``` Se você estiver usando um array para especificar as colunas, você também pode usar as chaves do array para especificar os aliases das colunas. Por exemplo, o código acima pode ser reescrito da seguinte forma, ```php $query->select(['user_id' => 'user.id', 'email']); ``` Se você não chamar o método [[yii\db\Query::select()|select()]] na criação da query, o `*` será selecionado, o que significa selecionar *todas* as colunas. Além dos nomes de colunas, você também pode selecionar expressões DB. Você deve usar o formato array quando utilizar uma expressão DB que contenha vírgula para evitar que sejam gerados nomes de colunas de forma equivocada. Por exemplo: ```php $query->select(["CONCAT(first_name, ' ', last_name) AS full_name", 'email']); ``` A partir da versão 2.0.1, você também pode selecionar sub-queries. Você deve especificar cada sub-query na forma de um objeto [[yii\db\Query]]. Por exemplo: ```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'); ``` Para utilizar a cláusula `distinct`, você pode chamar [[yii\db\Query::distinct()|distinct()]], como a seguir: ```php // SELECT DISTINCT `user_id` ... $query->select('user_id')->distinct(); ``` Você pode chamar [[yii\db\Query::addSelect()|addSelect()]] para selecionar colunas adicionais. Por exemplo: ```php $query->select(['id', 'username']) ->addSelect(['email']); ``` ### [[yii\db\Query::from()|from()]] O método [[yii\db\Query::from()|from()]] especifica o fragmento de uma instrução SQL `FROM`. Por exemplo: ```php // SELECT * FROM `user` $query->from('user'); ``` Você pode especificar todas tabelas a serem selecionadas a partir de uma string ou um array. O nome da tabela pode conter prefixos de esquema e/ou aliases de tabela, da mesma forma quando você escreve instruções SQL manualmente. Por exemplo: ```php $query->from(['public.user u', 'public.post p']); // é equivalente a: $query->from('public.user u, public.post p'); ``` Se você estiver usando o formato array, você também pode usar as chaves do array para especificar os aliases de tabelas, como mostrado a seguir: ```php $query->from(['u' => 'public.user', 'p' => 'public.post']); ``` Além de nome de tabelas, você também pode selecionar a partir de sub-queries especificando-o um objeto [[yii\db\Query]]. Por exemplo: ```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()]] O método [[yii\db\Query::where()|where()]] especifica o fragmento de uma instrução SQL `WHERE`. Você pode usar um dos três formatos para especificar uma condição `WHERE`: - formato string, ex., `'status=1'` - formato hash, ex. `['status' => 1, 'type' => 2]` - formato operador, ex. `['like', 'name', 'test']` #### Formato String Formato de string é mais usado para especificar condições `WHERE` muito simples. Esta forma é muito semelhante a condições `WHERE` escritas manualmente. Por exemplo: ```php $query->where('status=1'); // ou usar parâmetro para vincular os valores dinamicamente $query->where('status=:status', [':status' => $status]); ``` NÃO incorporar variáveis diretamente na condição como exemplificado abaixo, especialmente se os valores das variáveis vêm de entradas de dados dos usuários finais, porque isso vai fazer a sua aplicação ficar sujeita a ataques de injeção de SQL. ```php // Perigoso! NÃO faça isto a menos que você esteja muito certo que o $status deve ser um número inteiro. $query->where("status=$status"); ``` Ao usar parâmetro, você pode chamar [[yii\db\Query::params()|params()]] ou [[yii\db\Query::addParams()|addParams()]] para especificar os parâmetros separadamente. ```php $query->where('status=:status') ->addParams([':status' => $status]); ``` #### Formato Hash Formato HASH é mais usado para especificar múltiplos `AND` - sub-condições concatenadas, sendo cada uma afirmação simples de igualdade. É escrito como um array cujas chaves são nomes de coluna e os valores correspondem ao conteúdo destas colunas. Por exemplo: ```php // ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15)) $query->where([ 'status' => 10, 'type' => null, 'id' => [4, 8, 15], ]); ``` Como você pode ver, o query builder é inteligente o suficiente para lidar corretamente com valores que são nulos ou arrays. Você também pode usar sub-queries com o formato hash conforme mostrado abaixo: ```php $userQuery = (new Query())->select('id')->from('user'); // ...WHERE `id` IN (SELECT `id` FROM `user`) $query->where(['id' => $userQuery]); ``` #### Formato Operador Formato operador lhe permite especificar arbitrariamente condições de uma forma programática. Ele tem o seguinte formato: ```php [operator, operand1, operand2, ...] ``` onde cada um dos operandos pode ser especificado no formato string, formato hash ou formato operador recursivamente, enquanto o operador pode ser um dos seguintes procedimentos: - `and`: os operandos devem ser concatenados juntos usando `AND`. Por exemplo, `['and', 'id=1', 'id=2']` irá gerar `id=1 AND id=2`. Se um operando for um array, ele será convertido para string usando as regras descritas aqui. Por exemplo, `['and', 'type=1', ['or', 'id=1', 'id=2']]` irá gerar `type=1 AND (id=1 OR id=2)`. O método NÃO vai fazer qualquer tratamento de escapar caracteres ou colocar aspas. - `or`: similar ao operador `and` exceto pelo fato de que os operandos são concatenados usando `OR`. - `between`: o operando 1 deve ser um nome de coluna, e os operandos 2 e 3 devem ser os valores de início e fim. Por exemplo, `['between', 'id', 1, 10]` irá gerar `id BETWEEN 1 AND 10`. - `not between`: similar ao `between` exceto pelo fato de que `BETWEEN` é substituído por `NOT BETWEEN` na geração da condição. - `in`: o operando 1 deve ser um nome de coluna ou uma expressão DB. O operando 2 pode ser tanto um array ou um objeto `Query`. Será gerado uma condição `IN`. Se o operando 2 for um array, representará o intervalo dos valores que a coluna ou expressão DB devem ser; se o operando 2 for um objeto `Query`, uma sub-query será gerada e usada como intervalo da coluna ou expressão DB. Por exemplo, `['in', 'id', [1, 2, 3]]` irá gerar `id IN (1, 2, 3)`. O método fará o tratamento apropriado de aspas e escape de valores para o intervalo. O operador `in` também suporta colunas compostas. Neste caso, o operando 1 deve ser um array de colunas, enquanto o operando 2 deve ser um array de arrays ou um objeto `Query` representando o intervalo das colunas. - `not in`: similar ao operador `in` exceto pelo fato de que o `IN` é substituído por `NOT IN` na geração da condição. - `like`: o operando 1 deve ser uma coluna ou uma expressão DB, e o operando 2 deve ser uma string ou um array representando o valor que a coluna ou expressão DB devem atender. Por exemplo, `['like', 'name', 'tester']` irá gerar `name LIKE '%tester%'`. Quando a faixa de valor é dado como um array, múltiplos predicados `LIKE` serão gerados e concatenadas utilizando `AND`. Por exemplo, `['like', 'name', ['test', 'sample']]` irá gerar `name LIKE '%test%' AND name LIKE '%sample%'`. Você também pode fornecer um terceiro operando opcional para especificar como escapar caracteres especiais nos valores. O operando deve ser um array de mapeamentos de caracteres especiais. Se este operando não for fornecido, um mapeamento de escape padrão será usado. Você pode usar `false` ou um array vazio para indicar que os valores já estão escapados e nenhum escape deve ser aplicado. Note-se que ao usar um mapeamento de escape (ou o terceiro operando não é fornecido), os valores serão automaticamente fechado dentro de um par de caracteres percentuais. > Observação: Ao utilizar o SGDB PostgreSQL você também pode usar [`ilike`](http://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE) > em vez de `like` para diferenciar maiúsculas de minúsculas. - `or like`: similar ao operador `like` exceto pelo fato de que `OR` é usado para concatenar os predicados `LIKE` quando o operando 2 é um array. - `not like`: similar ao operador `like` exceto pelo fato de que `LIKE` é substituído por `NOT LIKE`. - `or not like`: similar ao operador `not like` exceto pelo fato de que `OR` é usado para concatenar os predicados `NOT LIKE`. - `exists`: requer um operador que deve ser uma instância de [[yii\db\Query]] representando a sub-query. Isto criará uma expressão `EXISTS (sub-query)`. - `not exists`: similar ao operador `exists` e cria uma expressão `NOT EXISTS (sub-query)`. - `>`, `<=`, ou qualquer outro operador válido que leva dois operandos: o primeiro operando deve ser um nome de coluna enquanto o segundo um valor. Ex., `['>', 'age', 10]` vai gerar `age>10`. #### Acrescentando Condições Você pode usar [[yii\db\Query::andWhere()|andWhere()]] ou [[yii\db\Query::orWhere()|orWhere()]] para acrescentar condições adicionais a uma condição já existente. Você pode chamá-los várias vezes para acrescentar várias condições separadamente. Por exemplo: ```php $status = 10; $search = 'yii'; $query->where(['status' => $status]); if (!empty($search)) { $query->andWhere(['like', 'title', $search]); } ``` Se o `$search` não estiver vazio, a seguinte instrução SQL será gerada: ```sql ... WHERE (`status` = 10) AND (`title` LIKE '%yii%') ``` #### Filtrar Condições Ao construir condições `WHERE` a partir de entradas de usuários finais, você geralmente deseja ignorar os valores vazios. Por exemplo, em um formulário de busca que lhe permite pesquisar por nome ou e-mail, você poderia ignorar as condições nome/e-mail se não houver entradas destes valores. Para atingir este objetivo utilize o método [[yii\db\Query::filterWhere()|filterWhere()]]: ```php // $username and $email são inputs dos usuário finais $query->filterWhere([ 'username' => $username, 'email' => $email, ]); ``` A única diferença entre [[yii\db\Query::filterWhere()|filterWhere()]] e [[yii\db\Query::where()|where()]] é que o primeiro irá ignorar valores vazios fornecidos na condição no [formato hash](#hash-format). Então se `$email` for vazio e `$username` não, o código acima resultará um SQL como: `...WHERE username=:username`. > Observação: Um valor é considerado vazio se ele for `null`, um array vazio, uma string vazia ou uma string que consiste em apenas espaços em branco. Assim como [[yii\db\Query::andWhere()|andWhere()]] e [[yii\db\Query::orWhere()|orWhere()]], você pode usar [[yii\db\Query::andFilterWhere()|andFilterWhere()]] e [[yii\db\Query::orFilterWhere()|orFilterWhere()]] para inserir condições de filtro adicionais. ### [[yii\db\Query::orderBy()|orderBy()]] O método [[yii\db\Query::orderBy()|orderBy()]] especifica o fragmento de uma instrução SQL `ORDER BY`. Por exemplo: ```php // ... ORDER BY `id` ASC, `name` DESC $query->orderBy([ 'id' => SORT_ASC, 'name' => SORT_DESC, ]); ``` No código acima, as chaves do array são nomes de colunas e os valores são a direção da ordenação. A constante PHP `SORT_ASC` indica ordem crescente e `SORT_DESC` ordem decrescente. Se `ORDER BY` envolver apenas nomes simples de colunas, você pode especificá-lo usando string, da mesma forma como faria escrevendo SQL manualmente. Por exemplo: ```php $query->orderBy('id ASC, name DESC'); ``` > Observação: Você deve usar o formato array se `ORDER BY` envolver alguma expressão DB. Você pode chamar [[yii\db\Query::addOrderBy()|addOrderBy()]] para incluir colunas adicionais para o fragmento `ORDER BY`. Por exemplo: ```php $query->orderBy('id ASC') ->addOrderBy('name DESC'); ``` ### [[yii\db\Query::groupBy()|groupBy()]] O método [[yii\db\Query::groupBy()|groupBy()]] especifica o fragmento de uma instrução SQL `GROUP BY`. Por exemplo: ```php // ... GROUP BY `id`, `status` $query->groupBy(['id', 'status']); ``` Se o `GROUP BY` envolver apenas nomes de colunas simples, você pode especificá-lo usando uma string, da mesma forma como faria escrevendo SQL manualmente. Por exemplo: ```php $query->groupBy('id, status'); ``` > Observação: Você deve usar o formato array se `GROUP BY` envolver alguma expressão DB. Você pode chamar [[yii\db\Query::addGroupBy()|addGroupBy()]] para incluir colunas adicionais ao fragmento `GROUP BY`. Por exemplo: ```php $query->groupBy(['id', 'status']) ->addGroupBy('age'); ``` ### [[yii\db\Query::having()|having()]] O método [[yii\db\Query::having()|having()]] especifica o fragmento de uma instrução SQL `HAVING`. Este método recebe uma condição que pode ser especificada da mesma forma como é feito para o [where()](#where). Por exemplo: ```php // ... HAVING `status` = 1 $query->having(['status' => 1]); ``` Por favor, consulte a documentação do [where()](#where) para mais detalhes de como especificar uma condição. Você pode chamar [[yii\db\Query::andHaving()|andHaving()]] ou [[yii\db\Query::orHaving()|orHaving()]] para incluir uma condição adicional para o fragmento `HAVING`. Por exemplo: ```php // ... HAVING (`status` = 1) AND (`age` > 30) $query->having(['status' => 1]) ->andHaving(['>', 'age', 30]); ``` ### [[yii\db\Query::limit()|limit()]] e [[yii\db\Query::offset()|offset()]] Os métodos [[yii\db\Query::limit()|limit()]] e [[yii\db\Query::offset()|offset()]] especificam os fragmentos de uma instrução SQL `LIMIT` e `OFFSET`. Por exemplo: ```php // ... LIMIT 10 OFFSET 20 $query->limit(10)->offset(20); ``` Se você especificar um limit ou offset inválido (Ex. um valor negativo), ele será ignorado. > Observação: Para SGDBs que não suportam `LIMIT` e `OFFSET` (ex. MSSQL), query builder irá gerar uma instrução SQL que emula o comportamento `LIMIT`/`OFFSET`. ### [[yii\db\Query::join()|join()]] O método [[yii\db\Query::join()|join()]] especifica o fragmento de uma instrução SQL `JOIN`. Por exemplo: ```php // ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id` $query->join('LEFT JOIN', 'post', 'post.user_id = user.id'); ``` O método [[yii\db\Query::join()|join()]] recebe quatro parâmetros: - `$type`: tipo do join, ex., `'INNER JOIN'`, `'LEFT JOIN'`. - `$table`: o nome da tabela a ser unida. - `$on`: opcional, a condição do join, isto é, o fragmento `ON`. Por favor, consulte [where()](#where) para detalhes sobre como especificar uma condição. - `$params`: opcional, os parâmetros a serem vinculados à condição do join. Você pode usar os seguintes métodos de atalho para especificar `INNER JOIN`, `LEFT JOIN` e `RIGHT JOIN`, respectivamente. - [[yii\db\Query::innerJoin()|innerJoin()]] - [[yii\db\Query::leftJoin()|leftJoin()]] - [[yii\db\Query::rightJoin()|rightJoin()]] Por exemplo: ```php $query->leftJoin('post', 'post.user_id = user.id'); ``` Para unir múltiplas tabelas, chame os métodos join acima multiplas vezes, uma para cada tabela. Além de unir tabelas, você também pode unir sub-queries. Para fazê-lo, especifique a sub-queries a ser unida como um objeto [[yii\db\Query]]. Por exemplo: ```php $subQuery = (new \yii\db\Query())->from('post'); $query->leftJoin(['u' => $subQuery], 'u.id = author_id'); ``` Neste caso, você deve colocar a sub-query em um array e usar as chaves do array para especificar o alias. ### [[yii\db\Query::union()|union()]] O método [[yii\db\Query::union()|union()]] especifica o fragmento de uma instrução SQL `UNION`. Por exemplo: ```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); ``` Você pode chamar [[yii\db\Query::union()|union()]] múltiplas vezes para acrescentar mais fragmentos `UNION`. ## Métodos Query [[yii\db\Query]] fornece um conjunto de métodos para diferentes propósitos da consulta: - [[yii\db\Query::all()|all()]]: retorna um array de linhas sendo cada linha um array de pares nome-valor. - [[yii\db\Query::one()|one()]]: retorna a primeira linha do resultado. - [[yii\db\Query::column()|column()]]: retorna a primeira coluna do resultado. - [[yii\db\Query::scalar()|scalar()]]: retorna um valor escalar localizado na primeira linha e coluna do primeiro resultado. - [[yii\db\Query::exists()|exists()]]: retorna um valor que indica se a consulta contém qualquer resultado. - [[yii\db\Query::count()|count()]]: retorna a quantidade de resultados da query. - Outros métodos de agregação da query, incluindo [[yii\db\Query::sum()|sum($q)]], [[yii\db\Query::average()|average($q)]], [[yii\db\Query::max()|max($q)]], [[yii\db\Query::min()|min($q)]]. O parâmetro `$q` é obrigatório para estes métodos e pode ser um nome de uma coluna ou expressão DB. Por exemplo: ```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(); ``` > Observação: O método [[yii\db\Query::one()|one()]] retorna apenas a primeira linha do resultado da query. Ele não adiciona `LIMIT 1` para a geração da sentença SQL. Isso é bom e preferível se você souber que a query retornará apenas uma ou algumas linhas de dados (Ex. se você estiver consultando com algumas chaves primárias). Entretanto, se a query pode retornar muitas linha de dados, você deve chamar `limit(1)` explicitamente para melhorar a performance. Ex., `(new \yii\db\Query())->from('user')->limit(1)->one()`. Todos estes métodos query recebem um parâmetro opcional `$db` que representa a [[yii\db\Connection|conexão do DB]] que deve ser usada para realizar uma consulta no DB. Se você omitir este parâmetro, o [componente da aplicação](structure-application-components.md) `db` será usado como a conexão do DB. Abaixo está um outro exemplo do método `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(); ``` Quando você chamar um método de [[yii\db\Query]], ele na verdade faz o seguinte trabalho por baixo dos panos: * Chama [[yii\db\QueryBuilder]] para gerar uma instrução SQL com base na atual construção de [[yii\db\Query]]; * Cria um objeto [[yii\db\Command]] com a instrução SQL gerada; * Chama um método query (ex. `queryAll()`) do [[yii\db\Command]] para executar a instrução SQL e retornar os dados. Algumas vezes, você pode querer examinar ou usar a instrução SQL construído a partir de um objeto [[yii\db\Query]]. Você pode atingir este objetivo com o seguinte código: ```php $command = (new \yii\db\Query()) ->select(['id', 'email']) ->from('user') ->where(['last_name' => 'Smith']) ->limit(10) ->createCommand(); // mostra a instrução SQL echo $command->sql; // Mostra os parâmetros que serão ligados print_r($command->params); // retorna todas as linhas do resultado da query $rows = $command->queryAll(); ``` ### Indexando os Resultados da Query Quando você chama [[yii\db\Query::all()|all()]], será retornado um array de linhas que são indexadas por inteiros consecutivos. Algumas vezes você pode querer indexa-los de forma diferente, tal como indexar por uma coluna ou valor de expressão em particular. Você pode atingir este objetivo chamando [[yii\db\Query::indexBy()|indexBy()]] antes de [[yii\db\Query::all()|all()]]. Por exemplo: ```php // retorna [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...] $query = (new \yii\db\Query()) ->from('user') ->limit(10) ->indexBy('id') ->all(); ``` Para indexar através de valores de expressão, passe uma função anônima para o método [[yii\db\Query::indexBy()|indexBy()]]: ```php $query = (new \yii\db\Query()) ->from('user') ->indexBy(function ($row) { return $row['id'] . $row['username']; })->all(); ``` A função anônima recebe um parâmetro `$row` que contém os dados da linha atual e deve devolver um valor escalar que irá ser utilizada como índice para o valor da linha atual. ### Batch Query (Consultas em Lote) Ao trabalhar com grandes quantidades de dados, métodos tais como [[yii\db\Query::all()]] não são adequados porque eles exigem carregar todos os dados na memória. Para manter o requisito de memória baixa, Yii fornece o chamado suporte batch query. Um batch query faz uso do cursor de dados e obtém dados em lotes. Batch query pode ser usado como a seguir: ```php use yii\db\Query; $query = (new Query()) ->from('user') ->orderBy('id'); foreach ($query->batch() as $users) { // $users é um array de 100 ou menos linha da tabela user } // ou se você quiser fazer uma iteração da linha uma por uma foreach ($query->each() as $user) { // $user representa uma linha de dados a partir da tabela user } ``` O método [[yii\db\Query::batch()]] and [[yii\db\Query::each()]] retorna um objeto [[yii\db\BatchQueryResult]] que implementa a interface `Iterator` e, assim, pode ser utilizado na construção do `foreach`. Durante a primeira iteração, uma consulta SQL é feita à base de dados. Os dados são, então, baixados em lotes nas iterações restantes. Por padrão, o tamanho do batch é 100, significando 100 linhas de dados que serão baixados a cada batch. Você pode mudar o tamanho do batch passando o primeiro parâmetro para os métodos `batch()` ou `each()`. Em comparação com o [[yii\db\Query::all()]], o batch query somente carrega 100 linhas de dados na memória a cada vez. Se você processar os dados e, em seguida, descartá-lo imediatamente, o batch query pode ajudar a reduzir o uso de memória. Se você especificar o resultado da query a ser indexado por alguma coluna via [[yii\db\Query::indexBy()]], o batch query ainda vai manter o índice adequado. Por exemplo: ```php $query = (new \yii\db\Query()) ->from('user') ->indexBy('username'); foreach ($query->batch() as $users) { // $users é indexado pela coluna "username" } foreach ($query->each() as $username => $user) { } ```