|
|
|
Constructor de Consultas
|
|
|
|
========================
|
|
|
|
|
|
|
|
> Nota: Esta sección está en desarrollo.
|
|
|
|
|
|
|
|
Yii proporciona una capa de acceso básico a bases de datos como se describe en la sección
|
|
|
|
[Objetos de Acceso a Bases de Datos](db-dao.md). La capa de acceso a bases de datos proporciona un método de bajo
|
|
|
|
nivel (low-level) para interaccionar con la base de datos. Aunque a veces puede ser útil la escritura de sentencias
|
|
|
|
SQLs puras, en otras situaciones puede ser pesado y propenso a errores. Otra manera de tratar con bases de datos puede
|
|
|
|
ser el uso de Constructores de Consultas (Query Builder). El Constructor de Consultas proporciona un medio orientado a
|
|
|
|
objetos para generar las consultas que se ejecutarán.
|
|
|
|
|
|
|
|
Un uso típico de Constructor de Consultas puede ser el siguiente:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$rows = (new \yii\db\Query())
|
|
|
|
->select('id, name')
|
|
|
|
->from('user')
|
|
|
|
->limit(10)
|
|
|
|
->all();
|
|
|
|
|
|
|
|
// que es equivalente al siguiente código:
|
|
|
|
|
|
|
|
$query = (new \yii\db\Query())
|
|
|
|
->select('id, name')
|
|
|
|
->from('user')
|
|
|
|
->limit(10);
|
|
|
|
|
|
|
|
// Crear un comando. Se puede obtener la consulta SQL actual utilizando $command->sql
|
|
|
|
$command = $query->createCommand();
|
|
|
|
|
|
|
|
// Ejecutar el comando:
|
|
|
|
$rows = $command->queryAll();
|
|
|
|
```
|
|
|
|
|
|
|
|
Métodos de Consulta
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
Como se puede observar, primero se debe tratar con [[yii\db\Query]]. En realidad, `Query` sólo se encarga de
|
|
|
|
representar diversa información de la consulta. La lógica para generar la consulta se efectúa mediante
|
|
|
|
[[yii\db\QueryBuilder]] cuando se llama al método `createCommand()`, y la ejecución de la consulta la efectúa
|
|
|
|
[[yii\db\Command]].
|
|
|
|
|
|
|
|
Se ha establecido, por convenio, que [[yii\db\Query]] proporcione un conjunto de métodos de consulta comunes que
|
|
|
|
construirán la consulta, la ejecutarán, y devolverán el resultado. Por ejemplo:
|
|
|
|
|
|
|
|
- [[yii\db\Query::all()|all()]]: construye la consulta, la ejecuta y devuelve todos los resultados en formato de array.
|
|
|
|
- [[yii\db\Query::one()|one()]]: devuelve la primera fila del resultado.
|
|
|
|
- [[yii\db\Query::column()|column()]]: devuelve la primera columna del resultado.
|
|
|
|
- [[yii\db\Query::scalar()|scalar()]]: devuelve la primera columna en la primera fila del resultado.
|
|
|
|
- [[yii\db\Query::exists()|exists()]]: devuelve un valor indicando si la el resultado devuelve algo.
|
|
|
|
- [[yii\db\Query::count()|count()]]: devuelve el resultado de la consulta `COUNT`. Otros métodos similares incluidos
|
|
|
|
son `sum($q)`, `average($q)`, `max($q)`, `min($q)`, que soportan las llamadas funciones de agregación. El parámetro
|
|
|
|
`$q` es obligatorio en estos métodos y puede ser el nombre de la columna o expresión.
|
|
|
|
|
|
|
|
Construcción de Consultas
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
A continuación se explicará como construir una sentencia SQL que incluya varias clausulas. Para simplificarlo, usamos
|
|
|
|
`$query` para representar el objeto [[yii\db\Query]]:
|
|
|
|
|
|
|
|
### `SELECT`
|
|
|
|
|
|
|
|
Para formar una consulta `SELECT` básica, se necesita especificar que columnas y de que tablas se seleccionarán:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select('id, name')
|
|
|
|
->from('user');
|
|
|
|
```
|
|
|
|
|
|
|
|
Las opciones de select se pueden especificar como una cadena de texto (string) separada por comas o como un array. La
|
|
|
|
sintaxis del array es especialmente útil cuando se forma la selección dinámicamente.
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select(['id', 'name'])
|
|
|
|
->from('user');
|
|
|
|
```
|
|
|
|
|
|
|
|
> Información: Se debe usar siempre el formato array si la clausula `SELECT` contiene expresiones SQL. Esto se debe a
|
|
|
|
que una expresión SQL como `CONCAT(first_name, last_name) AS full_name` puede contener comas. Si se junta con otra
|
|
|
|
cadena de texto de otra columna, puede ser que la expresión se divida en varias partes por comas, esto puede
|
|
|
|
conllevar a errores.
|
|
|
|
|
|
|
|
Cuando se especifican columnas, se pueden incluir los prefijos de las tablas o alias de columnas, ej. `user.id`,
|
|
|
|
`user.id AS user_id`. Si se usa un array para especificar las columnas, también se pueden usar las claves del array
|
|
|
|
para especificar los alias de columna, ej. `['user_id' => 'user.id', 'user_name' => 'user.name']`.
|
|
|
|
|
|
|
|
A partir de la versión 2.0.1, también se pueden seleccionar subconsultas como columnas. Por ejemplo:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$subQuery = (new Query)->select('COUNT(*)')->from('user');
|
|
|
|
$query = (new Query)->select(['id', 'count' => $subQuery])->from('post');
|
|
|
|
// $query representa la siguiente sentencia SQL:
|
|
|
|
// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`
|
|
|
|
```
|
|
|
|
|
|
|
|
Para seleccionar filas distintas, se puede llamar a `distinct()`, como se muestra a continuación:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select('user_id')->distinct()->from('post');
|
|
|
|
```
|
|
|
|
|
|
|
|
### `FROM`
|
|
|
|
|
|
|
|
Para especificar de que tabla(s) se quieren seleccionar los datos, se llama a `from()`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select('*')->from('user');
|
|
|
|
```
|
|
|
|
|
|
|
|
Se pueden especificar múltiples tablas usando una cadena de texto separado por comas o un array. Los nombres de tablas
|
|
|
|
pueden contener prefijos de esquema (ej. `'public.user'`) y/o alias de tablas (ej. `'user u'). El método
|
|
|
|
entrecomillara automáticamente los nombres de tablas a menos que contengan algún paréntesis (que significa que se
|
|
|
|
proporciona la tabla como una subconsulta o una expresión de BD). Por ejemplo:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select('u.*, p.*')->from(['user u', 'post p']);
|
|
|
|
```
|
|
|
|
|
|
|
|
Cuando se especifican las tablas como un array, también se pueden usar las claves de los arrays como alias de tablas
|
|
|
|
(si una tabla no necesita alias, no se usa una clave en formato texto). Por ejemplo:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select('u.*, p.*')->from(['u' => 'user', 'p' => 'post']);
|
|
|
|
```
|
|
|
|
|
|
|
|
Se puede especificar una subconsulta usando un objeto `Query`. En este caso, la clave del array correspondiente se
|
|
|
|
usará como alias para la subconsulta.
|
|
|
|
|
|
|
|
```php
|
|
|
|
$subQuery = (new Query())->select('id')->from('user')->where('status=1');
|
|
|
|
$query->select('*')->from(['u' => $subQuery]);
|
|
|
|
```
|
|
|
|
|
|
|
|
### `WHERE`
|
|
|
|
|
|
|
|
Habitualmente se seleccionan los datos basándose en ciertos criterios. El Constructor de Consultas tiene algunos
|
|
|
|
métodos útiles para especificarlos, el más poderoso de estos es `where`, y se puede usar de múltiples formas.
|
|
|
|
|
|
|
|
La manera más simple para aplicar una condición es usar una cadena de texto:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->where('status=:status', [':status' => $status]);
|
|
|
|
```
|
|
|
|
|
|
|
|
Cuando se usan cadenas de texto, hay que asegurarse que se unen los parámetros de la consulta, no crear una consulta
|
|
|
|
mediante concatenación de cadenas de texto. El enfoque anterior es seguro, el que se muestra a continuación, no lo es:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->where("status=$status"); // Peligroso!
|
|
|
|
```
|
|
|
|
|
|
|
|
En lugar de enlazar los valores de estado inmediatamente, se puede hacer usando `params` o `addParams`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->where('status=:status');
|
|
|
|
$query->addParams([':status' => $status]);
|
|
|
|
```
|
|
|
|
|
|
|
|
Se pueden establecer múltiples condiciones en `where` usando el *formato hash*.
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->where([
|
|
|
|
'status' => 10,
|
|
|
|
'type' => 2,
|
|
|
|
'id' => [4, 8, 15, 16, 23, 42],
|
|
|
|
]);
|
|
|
|
```
|
|
|
|
|
|
|
|
El código generará la el siguiente SQL:
|
|
|
|
|
|
|
|
```sql
|
|
|
|
WHERE (`status` = 10) AND (`type` = 2) AND (`id` IN (4, 8, 15, 16, 23, 42))
|
|
|
|
```
|
|
|
|
|
|
|
|
El valor NULO es un valor especial en las bases de datos, y el Constructor de Consultas lo gestiona inteligentemente.
|
|
|
|
Este código:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->where(['status' => null]);
|
|
|
|
```
|
|
|
|
|
|
|
|
da como resultado la siguiente cláusula WHERE:
|
|
|
|
|
|
|
|
```sql
|
|
|
|
WHERE (`status` IS NULL)
|
|
|
|
```
|
|
|
|
|
|
|
|
También se pueden crear subconsultas con objetos de tipo `Query` como en el siguiente ejemplo:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$userQuery = (new Query)->select('id')->from('user');
|
|
|
|
$query->where(['id' => $userQuery]);
|
|
|
|
```
|
|
|
|
|
|
|
|
que generará el siguiente código SQL:
|
|
|
|
|
|
|
|
```sql
|
|
|
|
WHERE `id` IN (SELECT `id` FROM `user`)
|
|
|
|
```
|
|
|
|
|
|
|
|
Otra manera de usar el método es el formato de operando que es `[operator, operand1, operand2, ...]`.
|
|
|
|
|
|
|
|
El operando puede ser uno de los siguientes (ver también [[yii\db\QueryInterface::where()]]):
|
|
|
|
|
|
|
|
- `and`: los operandos deben concatenerase usando `AND`. por ejemplo, `['and', 'id=1', 'id=2']` generará
|
|
|
|
`id=1 AND id=2`. Si el operando es un array, se convertirá en una cadena de texto usando las reglas aquí descritas.
|
|
|
|
Por ejemplo, `['and', 'type=1', ['or', 'id=1', 'id=2']]` generará `type=1 AND (id=1 OR id=2)`. El método no
|
|
|
|
ejecutará ningún filtrado ni entrecomillado.
|
|
|
|
|
|
|
|
- `or`: similar al operando `and` exceptuando que los operando son concatenados usando `OR`.
|
|
|
|
|
|
|
|
- `between`: el operando 1 debe ser el nombre de columna, y los operandos 2 y 3 deben ser los valores iniciales y
|
|
|
|
finales del rango en el que se encuentra la columna. Por ejemplo, `['between', 'id', 1, 10]` generará
|
|
|
|
`id BETWEEN 1 AND 10`.
|
|
|
|
|
|
|
|
- `not between`: similar a `between` exceptuando que `BETWEEN` se reemplaza por `NOT BETWEEN` en la condición
|
|
|
|
generada.
|
|
|
|
|
|
|
|
- `in`: el operando 1 debe ser una columna o una expresión de BD. El operando 2 puede ser un array o un objeto de tipo
|
|
|
|
`Query`. Generará una condición `IN`. Si el operando 2 es un array, representará el rango de valores que puede
|
|
|
|
albergar la columna o la expresión de BD; Si el operando 2 es un objeto de tipo `Query`, se generará una subconsulta
|
|
|
|
y se usará como rango de la columna o de la expresión de BD. Por ejemplo, `['in', 'id', [1, 2, 3]]` generará
|
|
|
|
`id IN (1, 2, 3)`. El método entrecomillará adecuadamente el nombre de columna y filtrará los valores del rango. El
|
|
|
|
operando `in` también soporta columnas compuestas. En este caso, el operando 1 debe se un array de columnas,
|
|
|
|
mientras que el operando 2 debe ser un array de arrays o un objeto de tipo `Query` que represente el rango de las
|
|
|
|
columnas.
|
|
|
|
|
|
|
|
- `not in`: similar que el operando `in` exceptuando que `IN` se reemplaza por `NOT IN` en la condición generada.
|
|
|
|
|
|
|
|
- `like`: el operando 1 debe ser una columna o una expresión de BD, y el operando 2 debe ser una cadena de texto o un
|
|
|
|
array que represente los valores a los que tienen que asemejarse la columna o la expresión de BD.Por ejemplo,
|
|
|
|
`['like', 'name', 'tester']` generará `name LIKE '%tester%'`. Cuando se da el valor rango como un array, se
|
|
|
|
generarán múltiples predicados `LIKE` y se concatenaran usando `AND`. Por ejemplo,
|
|
|
|
`['like', 'name', ['test', 'sample']]` generará `name LIKE '%test%' AND name LIKE '%sample%'`. También se puede
|
|
|
|
proporcionar un tercer operando opcional para especificar como deben filtrarse los caracteres especiales en los
|
|
|
|
valores. El operando debe se un array que mapeen los caracteres especiales a sus caracteres filtrados asociados. Si
|
|
|
|
no se proporciona este operando, se aplicará el mapeo de filtrado predeterminado. Se puede usar `false` o un array
|
|
|
|
vacío para indicar que los valores ya están filtrados y no se necesita aplicar ningún filtro. Hay que tener en
|
|
|
|
cuenta que cuando se usa un el mapeo de filtrado (o no se especifica el tercer operando), los valores se encerraran
|
|
|
|
automáticamente entre un par de caracteres de porcentaje.
|
|
|
|
|
|
|
|
> Nota: Cuando se usa PostgreSQL también se puede usar
|
|
|
|
[`ilike`](http://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE) en lugar de `like` para
|
|
|
|
filtrar resultados insensibles a mayúsculas (case-insensitive).
|
|
|
|
|
|
|
|
- `or like`: similar al operando `like` exceptuando que se usa `OR` para concatenar los predicados `LIKE` cuando haya
|
|
|
|
un segundo operando en un array.
|
|
|
|
|
|
|
|
- `not like`: similar al operando `like` exceptuando que se usa `LIKE` en lugar de `NOT LIKE` en las condiciones
|
|
|
|
generadas.
|
|
|
|
|
|
|
|
- `or not like`: similar al operando `not like` exceptuando que se usa `OR` para concatenar los predicados `NOT LIKE`.
|
|
|
|
|
|
|
|
- `exists`: requiere un operando que debe ser una instancia de [[yii\db\Query]] que represente la subconsulta. Esto
|
|
|
|
generará una expresión `EXISTS (sub-query)`.
|
|
|
|
|
|
|
|
- `not exists`: similar al operando `exists` y genera una expresión `NOT EXISTS (sub-query)`.
|
|
|
|
|
|
|
|
Adicionalmente se puede especificar cualquier cosa como operando:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select('id')
|
|
|
|
->from('user')
|
|
|
|
->where(['>=', 'id', 10]);
|
|
|
|
```
|
|
|
|
|
|
|
|
Cuyo resultado será:
|
|
|
|
|
|
|
|
```sql
|
|
|
|
SELECT id FROM user WHERE id >= 10;
|
|
|
|
```
|
|
|
|
|
|
|
|
Si se construyen partes de una condición dinámicamente, es muy convenientes usar `andWhere()` y `orWhere()`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$status = 10;
|
|
|
|
$search = 'yii';
|
|
|
|
|
|
|
|
$query->where(['status' => $status]);
|
|
|
|
if (!empty($search)) {
|
|
|
|
$query->andWhere(['like', 'title', $search]);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
En el caso que `$search` no este vacío, se generará el siguiente código SQL:
|
|
|
|
|
|
|
|
```sql
|
|
|
|
WHERE (`status` = 10) AND (`title` LIKE '%yii%')
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Construcción de Condiciones de Filtro
|
|
|
|
|
|
|
|
Cuando se generan condiciones de filtro basadas en datos recibidos de usuarios (inputs), a menudo se quieren gestionar
|
|
|
|
de forma especial las "datos vacíos" para ignorarlos en los filtros. Por ejemplo, teniendo un formulario HTML que
|
|
|
|
obtiene el nombre de usuario y la dirección de correo electrónico. Si el usuario solo rellena el campo de nombre de
|
|
|
|
usuario, se puede querer generar una consulta para saber si el nombre de usuario recibido es valido. Se puede usar
|
|
|
|
`filterWhere()` para conseguirlo:
|
|
|
|
|
|
|
|
```php
|
|
|
|
// $username y $email son campos de formulario rellenados por usuarios
|
|
|
|
$query->filterWhere([
|
|
|
|
'username' => $username,
|
|
|
|
'email' => $email,
|
|
|
|
]);
|
|
|
|
```
|
|
|
|
|
|
|
|
El método `filterWhere()` es muy similar al método `where()`. La principal diferencia es que el `filterWhere()`
|
|
|
|
eliminará los valores vacíos de las condiciones proporcionadas. Por lo tanto si `$email` es "vació", la consulta
|
|
|
|
resultante será `...WHERE username=:username`; y si tanto `$username` como `$email` son "vacías", la consulta no
|
|
|
|
tendrá `WHERE`.
|
|
|
|
|
|
|
|
Decimos que un valor es *vacío* si es nulo, una cadena de texto vacía, una cadena de texto que consista en espacios en
|
|
|
|
blanco o un array vacío.
|
|
|
|
|
|
|
|
También se pueden usar `andFilterWhere()` y `orFilterWhere()` para añadir más condiciones de filtro.
|
|
|
|
|
|
|
|
### `ORDER BY`
|
|
|
|
|
|
|
|
Se pueden usar `orderBy` y `addOrderBy` para ordenar resultados:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->orderBy([
|
|
|
|
'id' => SORT_ASC,
|
|
|
|
'name' => SORT_DESC,
|
|
|
|
]);
|
|
|
|
```
|
|
|
|
|
|
|
|
Aquí estamos ordenando por `id` ascendente y después por `name` descendente.
|
|
|
|
|
|
|
|
### `GROUP BY` and `HAVING`
|
|
|
|
|
|
|
|
Para añadir `GROUP BY` al SQL generado se puede usar el siguiente código:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->groupBy('id, status');
|
|
|
|
```
|
|
|
|
|
|
|
|
Si se quieren añadir otro campo después de usar `groupBy`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->addGroupBy(['created_at', 'updated_at']);
|
|
|
|
```
|
|
|
|
|
|
|
|
Para añadir la condición `HAVING` se pueden usar los métodos `having` y `andHaving` y `orHaving`. Los parámetros para
|
|
|
|
ellos son similares a los del grupo de métodos `where`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->having(['status' => $status]);
|
|
|
|
```
|
|
|
|
|
|
|
|
### `LIMIT` and `OFFSET`
|
|
|
|
|
|
|
|
Para limitar el resultado a 10 filas se puede usar `limit`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->limit(10);
|
|
|
|
```
|
|
|
|
|
|
|
|
Para saltarse las 100 primeras filas, se puede usar:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->offset(100);
|
|
|
|
```
|
|
|
|
|
|
|
|
### `JOIN`
|
|
|
|
|
|
|
|
Las clausulas `JOIN` se generan en el Constructor de Consultas usando el método join aplicable:
|
|
|
|
|
|
|
|
- `innerJoin()`
|
|
|
|
- `leftJoin()`
|
|
|
|
- `rightJoin()`
|
|
|
|
|
|
|
|
Este left join selecciona los datos desde dos tablas relacionadas en una consulta:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->select(['user.name AS author', 'post.title as title'])
|
|
|
|
->from('user')
|
|
|
|
->leftJoin('post', 'post.user_id = user.id');
|
|
|
|
```
|
|
|
|
|
|
|
|
En el código, el primer parámetro del método `leftjoin` especifica la tabla a la que aplicar el join. El segundo
|
|
|
|
parámetro, define la condición del join.
|
|
|
|
|
|
|
|
Si la aplicación de bases de datos soporta otros tipos de joins, se pueden usar mediante el método `join` genérico:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->join('FULL OUTER JOIN', 'post', 'post.user_id = user.id');
|
|
|
|
```
|
|
|
|
|
|
|
|
El primer argumento es el tipo de join a realizar. El segundo es la tabla a la que aplicar el join, y el tercero es la condición:
|
|
|
|
|
|
|
|
Como en `FROM`, también se pueden efectuar joins con subconsultas. Para hacerlo, se debe especificar la subconsulta
|
|
|
|
como un array que tiene que contener un elemento. El valor del array tiene que ser un objeto de tipo `Query` que
|
|
|
|
represente la subconsulta, mientras que la clave del array es el alias de la subconsulta. Por ejemplo:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query->leftJoin(['u' => $subQuery], 'u.id=author_id');
|
|
|
|
```
|
|
|
|
|
|
|
|
### `UNION`
|
|
|
|
|
|
|
|
En SQL `UNION` agrega resultados de una consulta a otra consulta. Las columnas devueltas por ambas consultas deben
|
|
|
|
coincidir. En Yii para construirla, primero se pueden formar dos objetos de tipo query y después usar el método
|
|
|
|
`union`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$query = new Query();
|
|
|
|
$query->select("id, category_id as type, name")->from('post')->limit(10);
|
|
|
|
|
|
|
|
$anotherQuery = new Query();
|
|
|
|
$anotherQuery->select('id, type, name')->from('user')->limit(10);
|
|
|
|
|
|
|
|
$query->union($anotherQuery);
|
|
|
|
```
|
|
|
|
|
|
|
|
Consulta por Lotes
|
|
|
|
---------------
|
|
|
|
|
|
|
|
Cuando se trabaja con grandes cantidades de datos, los métodos como [[yii\db\Query::all()]] no son adecuados ya que
|
|
|
|
requieren la carga de todos los datos en memoria. Para mantener los requerimientos de memoria reducidos, Yii
|
|
|
|
proporciona soporte a las llamadas consultas por lotes (batch query). Una consulta por lotes usa un cursor de datos y
|
|
|
|
recupera los datos en bloques.
|
|
|
|
|
|
|
|
Las consultas por lotes se pueden usar del siguiente modo:
|
|
|
|
|
|
|
|
```php
|
|
|
|
use yii\db\Query;
|
|
|
|
|
|
|
|
$query = (new Query())
|
|
|
|
->from('user')
|
|
|
|
->orderBy('id');
|
|
|
|
|
|
|
|
foreach ($query->batch() as $users) {
|
|
|
|
// $users is an array of 100 or fewer rows from the user table
|
|
|
|
}
|
|
|
|
|
|
|
|
// o si se quieren iterar las filas una a una
|
|
|
|
foreach ($query->each() as $user) {
|
|
|
|
// $user representa uno fila de datos de la tabla user
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Los métodos [[yii\db\Query::batch()]] y [[yii\db\Query::each()]] devuelven un objeto [[yii\db\BatchQueryResult]] que
|
|
|
|
implementa una interfaz `Iterator` y así se puede usar en el constructor `foreach`. Durante la primera iteración, se
|
|
|
|
efectúa una consulta SQL a la base de datos. Desde entonces, los datos se recuperan por lotes en las iteraciones. El
|
|
|
|
tamaño predeterminado de los lotes es 100, que significa que se recuperan 100 filas de datos en cada lote. Se puede
|
|
|
|
modificar el tamaño de los lotes pasando pasando un primer parámetro a los métodos `batch()` o `each()`.
|
|
|
|
|
|
|
|
En comparación con [[yii\db\Query::all()]], las consultas por lotes sólo cargan 100 filas de datos en memoria cada
|
|
|
|
vez. Si el procesan los datos y después se descartan inmediatamente, las consultas por lotes, pueden ayudar a mantener
|
|
|
|
el uso de memora bajo un limite.
|
|
|
|
|
|
|
|
Si se especifica que el resultado de la consulta tiene que ser indexado por alguna columna mediante
|
|
|
|
[[yii\db\Query::indexBy()]], las consultas por lotes seguirán manteniendo el indice adecuado. Por ejemplo,
|
|
|
|
|
|
|
|
```php
|
|
|
|
use yii\db\Query;
|
|
|
|
|
|
|
|
$query = (new Query())
|
|
|
|
->from('user')
|
|
|
|
->indexBy('username');
|
|
|
|
|
|
|
|
foreach ($query->batch() as $users) {
|
|
|
|
// $users esta indexado en la columna "username"
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($query->each() as $username => $user) {
|
|
|
|
}
|
|
|
|
```
|