¿Cómo puedo guardar matrices de valores en la base de datos?

8

Estoy tratando de guardar múltiples valores de un área de texto en una tabla de base de datos.

Uso el siguiente código, pero siento que es el camino equivocado.

foreach ($user_emails as $key => $value) {
  $insert_banned_emails = db_insert('banned_users');
  $insert_banned_emails
    ->fields(array(
      'email' => $value,
    ))
    ->execute();
}

¿Hay alguna forma diferente de lograr el mismo resultado?

Mohamed Ibrahim
fuente

Respuestas:

15

Yo usaría el siguiente código.

foreach ($user_emails as $value) {
  $query = db_insert('banned_users');
  $query->fields(array('email' => $value))->execute();
}

Alternativamente, puede usar el siguiente código.

$query = db_insert('banned_users')->fields(array('email'));

foreach ($user_emails as $value) {
  $query->values(array('email' => $value));
}

$query->execute();

Con MySQL, la consulta usa la sintaxis de valores múltiples.

Con otras bases de datos, las consultas ejecutadas serán una para cada llamada $query->values(), envueltas en una transacción. Esto significa que las consultas se revertirán cuando una de ellas falle. De hecho, el código ejecutado desde InsertQuery :: execute () es el siguiente.

  // Each insert happens in its own query in the degenerate case. However,
  // we wrap it in a transaction so that it is atomic where possible. On many
  // databases, such as SQLite, this is also a notable performance boost.
  $transaction = $this->connection->startTransaction();

  try {
    $sql = (string) $this;
    foreach ($this->insertValues as $insert_values) {
      $last_insert_id = $this->connection->query($sql, $insert_values, $this->queryOptions);
    }
  }
  catch (Exception $e) {
    // One of the INSERTs failed, rollback the whole batch.
    $transaction->rollback();
    // Rethrow the exception for the calling code.
    throw $e;
  }

En resumen, usaría el código que está utilizando si los valores insertados son independientes entre sí; Usaría el código que mostré cuando los valores dependen unos de otros.

En su caso, los correos electrónicos son independientes entre sí. Si usaría el segundo fragmento que mostré, la tabla de la base de datos contendrá todos los valores, cuando la subconsulta no falla, o ninguno cuando falla una sola subconsulta.

También podría usarlo drupal_write_record(), aunque prefiero los otros fragmentos.

foreach ($user_emails as $value) {
  drupal_write_record('banned_users', array('email' => $value));
}

Sin embargo, no veo ningún profesional en el uso de este fragmento.

Referencia

kiamlaluno
fuente
1
Lo siento, ¿qué pasa con el "Formulario de inserción múltiple" que se muestra en esta página de documentación? Dé 1 matriz de $valuesy solo llame a 1 execute(). drupal.org/node/310079 Esto se usa, por ejemplo, en la creación de bloques predeterminados de perfiles estándar.
tenken
2
Es cierto, puede llamar ->values(...)tantas veces como desee en un InsertQueryy preparará una consulta comoINSERT INTO x (field1, field2) VALUES ('val1', 'val2'), ('val3', 'val4'), etc
Clive
2
Bien, ahora recordé por qué descarté usar $query->values(): en la mayoría de mis casos, los valores que inserta mi código son independientes entre sí, y no quiero que un error con un valor provoque una reversión de los otros valores.
kiamlaluno
3

Esta es una versión similar a su código, pero un mejor rendimiento. Realmente no desea llamar a execute () miles de veces, solo necesita llamarlo una vez.

Referencia

$insert_banned_emails = db_insert('banned_users')->fields(array('email'));
foreach ($user_emails as $key => $value) {
  $insert_banned_emails->values(array(
    'email' => $value,
  ));               
}
$insert_banned_emails->execute();
donutdan4114
fuente
Me funcionó, excepto que arrojaría errores OOM cuando el número de líneas es demasiado grande (10,000) en mi caso. Entonces dividí eso en lotes de 1000 o menos para resolver ese problema.
Eduardo Chongkan