Una vez tienes instanciada una conexión a la base de datos, se pueden ejecutar consultas SQL tomando
los siguientes pasos:
Si no se quiere definir la conexión como un [componente de aplicación](structure-application-components.md), se puede
instanciar directamente:
1. Crea un [[yii\db\Command]] con SQL plano;
2. Vincula parámetros (opcional);
3. Llama a uno de los métodos de ejecución SQL con [[yii\db\Command]].
El siguiente ejemplo muestra varias maneras de obtener datos de una base de datos:
```php
$connection=new\yii\db\Connection([
'dsn'=>$dsn,
'username'=>$username,
'password'=>$password,
]);
$connection->open();
$db=newyii\db\Connection(...);
// retorna un conjunto de filas. Cada fila es un array asociativo de columnas de nombres y valores.
// un array vacío es retornado si no hay resultados
$posts=$db->createCommand('SELECT * FROM post')
->queryAll();
// retorna una sola fila (la primera fila)
// false es retornado si no hay resultados
$post=$db->createCommand('SELECT * FROM post WHERE id=1')
->queryOne();
// retorna una sola columna (la primera columna)
// un array vacío es retornado si no hay resultados
$titles=$db->createCommand('SELECT title FROM post')
->queryColumn();
// retorna un escalar
// false es retornado si no hay resultados
$count=$db->createCommand('SELECT COUNT(*) FROM post')
->queryScalar();
```
> Consejo: Si se necesita ejecutar una consulta SQL inmediatamente después de establecer la conexión
(ej. para establecer la zona horaria (timezone) o el juego de caracteres), se puede añadir el siguiente código en el
archivo de configuración de la aplicación:
> Nota: Para preservar la precisión, los datos obtenidos de las bases de datos son todos representados como cadenas, incluso si el tipo de columna correspondiente
a la base de datos es numérico.
> Consejo: Si necesitas ejecutar una consulta SQL inmediatamente después de establecer una conexión (ej., para establecer una zona horaria o un conjunto de caracteres),
> puedes hacerlo con el evento [[yii\db\Connection::EVENT_AFTER_OPEN]]. Por ejemplo,
Cuando la consulta que tiene que ser ejecutada devuelve un conjunto de filas, se usará `queryAll`:
Cuando creamos un comando DB para un SQL con parámetros, nosotros deberíamos casi siempre aprovechar el uso de los parámetros vinculados
para prevenir los ataques de inyección de SQL. Por ejemplo,
```php
$command=$connection->createCommand('SELECT * FROM post');
$posts=$command->queryAll();
$post=$db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
->bindValue(':id',$_GET['id'])
->bindValue(':status',1)
->queryOne();
```
Cuando la consulta que se ejecute devuelva una única fila, se usará `queryOne`:
En la sentencia SQL, puedes incrustar uno o múltiples parámetros placeholders (ej. `:id` en el ejemplo anterior). Un parámetro
placeholder debería ser una cadena que empiece con dos puntos. A continuación puedes llamar a uno de los siguientes métodos para
unir los valores de los parámetros vinculados:
*[[yii\db\Command::bindValue()|bindValue()]]: une un solo parámetro
*[[yii\db\Command::bindValues()|bindValues()]]: une múltiples parámetros en una sola llamada
*[[yii\db\Command::bindParam()|bindParam()]]: similar a [[yii\db\Command::bindValue()|bindValue()]] pero también
soporta las referencias de parámetros vinculados.
El siguiente ejemplo muestra formas alternativas de vincular parámetros:
```php
$command=$connection->createCommand('SELECT * FROM post WHERE id=1');
$post=$command->queryOne();
$params=[':id'=>$_GET['id'],':status'=>1];
$post=$db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
->bindValues($params)
->queryOne();
$post=$db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status',$params)
->queryOne();
```
Cuando la consulta devuelva múltiples filas pero solo una columna, se usará `queryColumn`:
La vinculación parámetros es implementada mediante [sentencias preparadas (prepared statements)](http://php.net/manual/es/mysqli.quickstart.prepared-statements.php).
Además de prevenir ataques de inyección de SQL, también puede mejorar el rendimiento preparando una sola vez una sentencia SQL y ejecutándola múltiples veces con diferentes
parámetros. Por ejemplo,
```php
$command=$connection->createCommand('SELECT title FROM post');
$titles=$command->queryColumn();
$command=$db->createCommand('SELECT * FROM post WHERE id=:id');
$post1=$command->bindValue(':id',1)->queryOne();
$post2=$command->bindValue(':id',2)->queryOne();
```
Cuando la consulta solo devuelva un valor escalar, se usará `queryScalar`:
Porque [[yii\db\Command::bindParam()|bindParam()]] soporta parámetros vinculados por referencias, el código de arriba también
puede ser escrito como lo siguiente:
```php
$command=$connection->createCommand('SELECT COUNT(*) FROM post');
$postCount=$command->queryScalar();
$command=$db->createCommand('SELECT * FROM post WHERE id=:id')
->bindParam(':id',$id);
$id=1;
$post1=$command->queryOne();
$id=2;
$post2=$command->queryOne();
```
### Ejecución de Consultas que No Devuelvan Valores
Observe que vincula el placeholder a la variable `$id` antes de la ejecución, y entonces cambia el valor de esa variable
antes de cada subsiguiente ejecución (esto se hace a menudo con bucles). Ejecutando consultas de esta manera puede ser
bastante más eficiente que ejecutar una nueva consulta para cada valor diferente del parámetro.
Si tu DBMS soporta Savepoint, puedes anidar múltiples transacciones como a continuación:
```php
$db->transaction(function($db){
// outer transaction
$db->transaction(function($db){
// inner transaction
});
});
```
O alternativamente,
```php
$outerTransaction=$db->beginTransaction();
try{
$db->createCommand($sql1)->execute();
$innerTransaction=$db->beginTransaction();
try{
$db->createCommand($sql2)->execute();
$innerTransaction->commit();
}catch(Exception$e){
$innerTransaction->rollBack();
}
$outerTransaction->commit();
}catch(Exception$e){
$outerTransaction->rollBack();
}
```
## Replicación y División Lectura-Escritura <span id="read-write-splitting"></span>
Muchos DBMS soportan [replicación de bases de datos](http://en.wikipedia.org/wiki/Replication_(computing)#Database_replication) para tener
una mejor disponibilidad de la base de datos y un mejor tiempo de respuesta del servidor. Con la replicación de bases
de datos, los datos están replicados en los llamados *servidores maestros* (master servers) y *servidores esclavos*
(slave servers). Todas las escrituras y actualizaciones deben hacerse en el servidor maestro mientras que las lecturas
(slave servers). Todas las escrituras y actualizaciones deben hacerse en el servidor maestro, mientras que las lecturas
se efectuarán en los servidores esclavos.
Para aprovechar las ventajas de la replicación de BBDD y lograr una división de lecuta-escritura, se puede configurar
Para aprovechar las ventajas de la replicación de la base de datos y lograr una división de lecuta-escritura, se puede configurar
el componente [[yii\db\Connection]] como se muestra a continuación:
```php
...
...
@@ -422,7 +476,7 @@ el componente [[yii\db\Connection]] como se muestra a continuación:
```
La configuración anterior especifica una configuración con un único maestro y múltiples esclavos. Uno de los esclavos
se conectará y se usará para ejecutar consultas de lectura mientras que el maestro se usará para realizar consultas de
se conectará y se usará para ejecutar consultas de lectura, mientras que el maestro se usará para realizar consultas de
escritura. De este modo la división de lectura-escritura se logra automáticamente con esta configuración, Por ejemplo,
```php
...
...
@@ -436,14 +490,13 @@ $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();
$db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute();
```
> Información: Las consultas realizadas ejecutando [[yii\db\Command::execute()]] se consideran consultas de escritura,
> Información: Las consultas realizadas llamando a [[yii\db\Command::execute()]] se consideran consultas de escritura,
mientras que todas las demás se ejecutan mediante alguno de los métodos "query" de [[yii\db\Command]] son consultas
de lectura. Se puede obtener la conexión de esclavo activa mediante `$db->slave`.
El componente `Connection` soporta el balanceo de carga y la conmutación de errores entre esclavos. Cuando se realiza
una consulta de lectura por primera vez, el componente `Connection` elegirá un esclavo aleatorio e intentará realizar
una conexión a este. Si está "muerto", se intentará con otro. Si no está disponible ningún esclavo, se conectará al
maestro. Configurando una [[yii\db\Connection::serverStatusCache|server status cache]], se recordarán los servidores
una conexión a este. Si el esclavo se encuentra "muerto", se intentará con otro. Si no está disponible ningún esclavo, se conectará al maestro. Configurando una [[yii\db\Connection::serverStatusCache|server status cache]], se recordarán los servidores
"muertos" por lo que no se intentará volver a conectar a ellos durante
[[yii\db\Connection::serverRetryInterval|certain period of time]].
...
...
@@ -451,7 +504,7 @@ maestro. Configurando una [[yii\db\Connection::serverStatusCache|server status c
para cada esclavo. Esto significa que si no se puede conectar a un esclavo en 10 segundos, este será considerado
como "muerto". Se puede ajustar el parámetro basado en el entorno actual.
También se pueden configurar múltiples parámetros para múltiples esclavos. Por ejemplo,
También se pueden configurar múltiples maestros con múltiples esclavos. Por ejemplo,
```php
[
...
...
@@ -501,8 +554,8 @@ cuando no se encuentra ningún maestro disponible se lanza una excepción.
ignorarán todas las otras propiedades que especifiquen una conexión de base de datos
(ej. `dsn`, `username`, `password`), junto con el mismo objeto `Connection`.
Las conexiones usan la conexión de maestro de forma predeterminada. Y todas las operaciones de BBDD que estén dentro
de una transacción, usaran la conexión de maestro. Por ejemplo,
Por defecto. las transacciones usan la conexión del maestro. Y dentro de una transacción, todas las operaciones de DB usarán
la conexión del maestro. Por ejemplo,
```php
// la transacción empieza con la conexión al maestro
...
...
@@ -520,7 +573,7 @@ try {
}
```
Si se quiere empezar la conexión con una conexión a un esclavo, se debe hacer explícitamente como se muestra a
Si se quiere empezar la transacción con una conexión a un esclavo, se debe hacer explícitamente como se muestra a