Como posso salvar matrizes de valores no banco de dados?

8

Estou tentando salvar vários valores de uma área de texto em uma tabela de banco de dados.

Eu uso o código a seguir, mas acho que é o caminho errado.

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

Existe uma maneira diferente de obter o mesmo resultado?

Mohamed Ibrahim
fonte

Respostas:

15

Eu usaria o seguinte código.

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

Como alternativa, você pode usar o seguinte código.

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

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

$query->execute();

Com o MySQL, a consulta usa a sintaxe de vários valores.

Com outros bancos de dados, as consultas executadas serão uma para cada chamada $query->values(), agrupadas em uma transação. Isso significa que as consultas serão revertidas quando uma delas falhar. De fato, o código executado em InsertQuery :: execute () é o seguinte.

  // 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;
  }

Em resumo, eu usaria o código que você está usando se os valores inseridos forem independentes um do outro; Eu usaria o código mostrado quando os valores dependem um do outro.

No seu caso, os e-mails são independentes um do outro. Se você usasse o segundo trecho que eu mostrei, a tabela do banco de dados conterá todos os valores, quando a subconsulta não falhar, ou nenhum quando uma única subconsulta falhar.

Você também pode usar drupal_write_record(), mesmo que eu prefira os outros trechos.

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

No entanto, não vejo nenhum profissional usando esse trecho.

Referência

kiamlaluno
fonte
11
Sinto muito, e o "Formulário de inserção múltipla" mostrado nesta página de documentação. Dê 1 matriz $valuese chame apenas 1 execute(). drupal.org/node/310079 Isso é usado, por exemplo, na criação de blocos padrão de perfis padrão.
Dez16
2
É verdade, você pode ligar ->values(...)quantas vezes quiser em um InsertQuerye ele preparará uma consulta comoINSERT INTO x (field1, field2) VALUES ('val1', 'val2'), ('val3', 'val4'), etc
Clive
2
Tudo bem, agora me lembrei por que descartei usando $query->values(): Na maioria dos meus casos, os valores que meu código está inserindo são independentes um do outro, e não quero que um erro com um valor cause uma reversão dos outros valores.
kiamlaluno
3

Esta é uma versão semelhante ao seu código, mas com melhor desempenho. Você realmente não deseja chamar execute () mil vezes, basta chamá-lo uma vez.

Referência

$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
fonte
Funcionou para mim, exceto que geraria erros de OOM quando o número de linhas for muito grande (10.000) no meu caso. Então, eu dividi isso em lotes de 1000 ou menos para resolver esse problema.
Eduardo Chongkan 5/10