2012-06-10 10 views
19

Estoy tratando de insertar algunas filas en la tabla MySQL usando Codeigniter y Active Records.INSERTAR IGNORAR usando Codeigniter

Código PHP

$data = array('......'); // some rows of data to insert 
$this->db->insert_batch('my_table', $data); 

Sin embargo, esto puede causar filas duplicadas que se insertan en la tabla. Para manejar la inserción de datos duplicados, planeo usar el comando INSERT IGNORE para no insertar la fila si la fila es un duplicado.

Problema: No puedo encontrar el equivalente INSERT IGNORE en Active Records y no quiero editar la clase Active Record. ¿Hay otras alternativas?

Lo siguiente parece interesante, pero si hago lo siguiente, ¿no se ejecutará la consulta dos veces?

$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 
+0

Después de leer todas las respuestas, vamos a resumir - NO hay buen método para hacer esto como un lote ': (' – Dan

+0

Probé el INSERT IGNORE con MySQL 5.5.42 y funciona. Sin embargo, hay un efecto secundario en el que el auto-incremento continuará aumentando incluso si no se insertó nada. –

Respuesta

2

Utilizando la técnica de la segunda idea, que podría generar una consulta al enrollarlo sobre la matriz y el uso de:

$this->db->query($query_string); 
+0

Si aún desea ejecutar solo un comando SQL para hacer la inserción de lote, puede usar el siguiente código ... '$ batch_assignment = $ this-> db-> set_insert_batch ($ batch_assignment) ; \t \t $ query = $ this-> db -> _ insert_batch ('users_to_tasks', array ('task_id', 'user_id'), $ this-> db-> ar_set); ' – Relequestual

-1

Evite las filas duplicadas estableciendo una clave única en la tabla de la base de datos para al menos uno de los campos.

+0

¿Esto provocará un error al intentar insertar una fila duplicada? – Nyxynyx

+0

No si configura la variable de configuración database.php para 'debug' en falso. – phirschybar

+3

El error aún se genera, codeigniter simplemente lo ignora; esta no es una gran solución –

34

No utilice insert_batch, ya que en realidad se ejecuta la consulta. ¿Quieres 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); 

ACTUALIZACIÓN: Esto no funciona para consultas por lotes, sólo una fila a la vez.

6

en las cargas por lotes que pueda necesitar algo más parecido a:

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

ACTUALIZACIÓN: Versión uso que dcostalis (en los comentarios debajo de este), escalará mucho mejor :-)

+0

Utilicé esto como una solución rápida para mi problema (inserto solo una pequeña matriz), no estoy seguro de lo bien que esto se escalaría con una gran matriz. – Danny

+1

'$ sql =" "; foreach ($ data as $ data_item) { $ insert_query = $ this-> db-> insert_string ('my_table', $ data_item); $ sql. = Str_replace ('INSERT INTO', 'INSERT IGNORE INTO', $ insert_query); } $ this-> db-> query ($ sql); ' –

6

Como también encontré un problema similar, finalmente elegí una solución un poco más "elegante" como la de abajo. Una consulta insert_batch completo que es algo que utiliza la respuesta de cohetes y transacciones:

$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(); 

que también envolver todo en una transacción que resulte en una consulta más rápida como hacer un insert_batch(). Bueno, no tan rápido como insert_batch() pero más rápido que una sola consulta para cada entrada, por supuesto. Espero que ayude a alguien.

0

No es muy recomendable, pero aquí hay un truco para preservar la inserción por lotes (que es WRT más eficiente 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); 
} 
+0

im usando ajax, y aún obtengo el error duplicado .. :( No quiero obtener el error duplicado, simplemente mueva a insertar ignore si existe duplicado .. –

+0

lo que sucede en el navegador no afectará el comportamiento de mysql. ¿Intentó el método que proponía? Si desea suprimir los errores de DB, ajuste la configuración de configuración de DB. Si maneja el error, debe pasar la inserción. – Fakeer

0

Para ello hice esta función auxiliar:

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

Se puede hacer la inserción de lote y la inserción de lote con ignorar. Para evitar filas duplicadas, debe establecer una clave única en la tabla de la base de datos para un campo primario.

0

Esto es básicamente una modificación de la sugerencia de Rocket Hazmat, que es genial, pero no tiene en cuenta el hecho de que str_replace opera en toda la cadena y puede afectar inadvertidamente los datos.

$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); 
-1

yo también tuve mismo problema lo que me ayudó es:

abierta: CodeIgniter/Sistema/Base de Datos/DB_query_builder.php:

encuentran

protected function _insert_batch($table, $keys, $values) 
    { 
     return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values); 
    } 

y sustituirlo por :

protected function _insert_batch($table, $keys, $values) 
{ 
    return 'INSERT IGNORE INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values); 
} 
+0

Esto funcionaría, pero no es una gran solución. Llega la actualización de CI que no recuerdas hacer esto, has generado un problema ... – pgee70

0

añadir a presentar DB_query_builder.php

este 2 funciones

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

usando:

$this->db->insert_ignore_batch('TableName', $data); 
Cuestiones relacionadas