Я пытаюсь вставить несколько строк в таблицу MySQL, используя Codeigniter и Active Records.
PHP-код
$data = array('......'); // some rows of data to insert $this->db->insert_batch('my_table', $data);
Однако это может привести к тому, что в таблицу будут вставлены повторяющиеся строки. Чтобы обрабатывать вставку дублированных данных, я планирую использовать команду INSERT IGNORE
чтобы не вставлять строку, если строка является дубликатом.
Проблема: я не могу найти эквивалент INSERT IGNORE
в Active Records и не хочу редактировать класс Active Record. Есть ли другие альтернативы?
Следующее выглядит интересным, но если я сделаю следующее, не будет ли запрос выполняться дважды?
$insert_query = $this->db->insert_batch('my_table', $data); // QUERY RUNS ONCE $insert_query = str_replace('INSERT INTO','INSERT IGNORE INTO',$insert_query); $this->db->query($insert_query); // QUERY RUNS A SECOND TIME
Используя технику вашей второй идеи, вы можете сгенерировать запрос, перейдя по массиву и используя:
$this->db->query($query_string);
Не используйте insert_batch
, поскольку он действительно запускает запрос. Вы хотите, чтобы insert_string
$insert_query = $this->db->insert_string('my_table', $data); $insert_query = str_replace('INSERT INTO','INSERT IGNORE INTO',$insert_query); $this->db->query($insert_query);
UPDATE : это не работает для пакетных запросов, только по одной строке за раз.
Для пакетных загрузок вам может понадобиться нечто большее:
foreach ($data as $data_item) { $insert_query = $this->db->insert_string('my_table', $data_item); $insert_query = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $insert_query); $this->db->query($insert_query); }
UPDATE: используйте версию dcostalis (в комментариях ниже этого), она будет масштабироваться намного лучше 🙂
Поскольку я также столкнулся с подобной проблемой, я, наконец, выбрал немного более «элегантное» решение, подобное приведенному ниже. Полный запрос insert_batch, который использует ответ Rocket и транзакции:
$this->db->trans_start(); foreach ($items as $item) { $insert_query = $this->db->insert_string('table_name', $item); $insert_query = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $insert_query); $this->db->query($insert_query); } $this->db->trans_complete();
Это также приведет к тому, что транзакция завершится быстрее, чем в insert_batch (). Ну не так быстро, как insert_batch (), но быстрее, чем один запрос для каждой записи курса. Надеюсь, это поможет кому-то.
Просто нашел это, может помочь http://www.amplio.ch/blog/web-development/codeigniter-active-record-class-and-insert-ignore/ , это, однако,
Не рекомендуется, но вот взломать пакетную вставку (что более эффективно для Mysql)
// try to insert as usual first $this->db->insert_batch('my_table', $data); // if it fails resort to IGNORE if($this->db->_error_message()) { $sql = $this->db->last_query(); $sql = str_replace('INSERT INTO', 'INSERT IGNORE INTO', $sql); $this->db->query($sql); }
Для этого я сделал эту вспомогательную функцию:
function insert_batch_string($table='',$data=[],$ignore=false){ $CI = &get_instance(); $sql = ''; if ($table && !empty($data)){ $rows = []; foreach ($data as $row) { $insert_string = $CI->db->insert_string($table,$row); if(empty($rows) && $sql ==''){ $sql = substr($insert_string,0,stripos($insert_string,'VALUES')); } $rows[] = trim(substr($insert_string,stripos($insert_string,'VALUES')+6)); } $sql.=' VALUES '.implode(',',$rows); if ($ignore) $sql = str_ireplace('INSERT INTO', 'INSERT IGNORE INTO', $sql); } return $sql; }
Он может вставлять пакетную вставку и пакетную вставку с игнорированием. Чтобы избежать дублирования строк, вы должны установить уникальный ключ в таблице базы данных для первичного поля.
Это, по сути, модификация предложения Rocket Hazmat, что отлично, но не учитывает тот факт, что str_replace работает со всей строкой и может непреднамеренно повлиять на данные.
$insert_query = $this->db->insert_string('my_table', $data); $insert_query = preg_replace('/INSERT INTO/','INSERT IGNORE INTO',$insert_query,1); $this->db->query($insert_query);
добавить в файл DB_query_builder.php
эти 2 функции
public function insert_ignore_batch($table = '', $set = NULL, $escape = NULL) { if ($set !== NULL) { $this->set_insert_batch($set, '', $escape); } if (count($this->qb_set) === 0) { // No valid data array. Folds in cases where keys and values did not match up return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE; } if ($table === '') { if ( ! isset($this->qb_from[0])) { return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; } $table = $this->qb_from[0]; } // Batch this baby $affected_rows = 0; for ($i = 0, $total = count($this->qb_set); $i < $total; $i += 100) { $this->query($this->_insert_ignore_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, 100))); $affected_rows += $this->affected_rows(); } $this->_reset_write(); return $affected_rows; } protected function _insert_ignore_batch($table, $keys, $values) { return 'INSERT IGNORE INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values); }
с помощью:
$this->db->insert_ignore_batch('TableName', $data);
Избегайте дублирования строк, установив уникальный ключ в таблице базы данных по крайней мере для одного из полей.
У меня тоже была такая же проблема, что мне помогло:
Открыть: Codeigniter / system / database / DB_query_builder.php :
найти
protected function _insert_batch($table, $keys, $values) { return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values); }
и заменить на:
protected function _insert_batch($table, $keys, $values) { return 'INSERT IGNORE INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values); }