2009-10-31 54 views
5

Estoy analizando rutinariamente un feed json y necesito insertar solo los usuarios más nuevos del feed e ignorar a los usuarios existentes.SQL: ¿Insertar solo nuevas filas/registros en una tabla?

creo que lo que necesito es ON DUPLICATE KEY UPDATE o INSERT IGNORE basado en algunas búsquedas pero no estoy muy seguro de que es por eso que estoy pidiendo - así por ejemplo:

users 
1  John 
2  Bob 

JSON parcial:

{ userid:1, name:'John' }, 
{ userid:2, name:'Bob' }, 
{ userid:3, name:'Jeff' } 

De esta alimentación solo quiero insertar Jeff. Podría hacer un simple bucle entre todos los usuarios y hacer una simple consulta SELECT y ver si la identificación del usuario ya está en la tabla, si no hago un INSERT, sin embargo, sospecho que no será un método eficiente y práctico.

Por cierto, estoy usando Zend_Db para la interacción de la base de datos, si alguien quisiera atender una respuesta específica :) Sin embargo, no me importa una solución estratégica genérica.

Respuesta

5

La alternativa ON DUPLICATE KEY UPDATE le permite referirse la actualización frente a la decisión de inserción a la base de datos:

INSERT INTO table (userid, name) VALUES (2, 'Bobby'); 
    ON DUPLICATE KEY UPDATE name = 'Bobby'; 

actualizaría el campo de nombre a 'Bobby', si una entrada con ID de usuario 2 ya existe.

Se puede utilizar como una alternativa a la INSERT IGNORE si se suministra una operación no eficaz para la actualización:

INSERT INTO table (userid, name) VALUES (2, 'Bobby'); 
    ON DUPLICATE KEY UPDATE name = name; 

Esto haría nada si identificador de usuario 2 ya existe, evitando así la advertencia y deglución de los otros errores que obtendría al usar INSERT IGNORE.


Otra alternativa sería REPLACE:

REPLACE INTO table (userid, name) VALUES (2, 'Bobby'); 

Esto haría una inserción normal si el ID de usuario 2 no existe todavía. Si existe, primero eliminará la entrada anterior y luego insertará una nueva.


Tenga en cuenta que ambas versiones son extensiones específicas de MySQL para SQL.

0

En el ciclo, puede hacer una actualización primero, si no hay una fila afectada, significa que es una entrada nueva. Mantenga un registro de esas 'actualizaciones fallidas' y luego haga una inserción de ellas como nuevas entradas.

No estoy familiarizado con Zend_Db pero supongo que puede devolver si una actualización afectó la cantidad de fila (s).

+0

De alguna manera leí mal la pregunta por la necesidad de actualizar los usuarios existentes si se encuentra, de lo contrario inserte como nuevo. bien ... down-votado –

2

es necesario definir ID de usuario como clave principal o único y usar algo como:

INSERT IGNORE INTO table (userid, name) VALUES (2, 'Bob'); 

Si el ID de usuario 2 ya existe, ignorar y pasar a la próxima inserción.

También puede usar ON DUPLICATE KEY UPDATE en el esquema de su tabla. Otra alternativa podría ser utilizar la sintaxis REPLACE INTO.

REPLACE INTO table (userid, name) VALUES (2, 'Bob'); 

Esto intenta insertar, si el registro ya existe, eliminarlo antes de volver a introducirlo.

+0

¿Se admite 'INSERT IGNORE' en Zend_Db? –

+0

Honestamente, NoFId. –

+0

El 'IGNORE' hará que el motor db emita advertencias en lugar de errores, por lo que la ejecución de la consulta no se detendrá. Un inconveniente de este enfoque es que * cualquier * error encontrado será ignorado/convertido en advertencia, no solo en una clave duplicada. –

4

Para Zend Framework, lo que hago es un try/catch de la instrucción de inserción y tomar medidas adicionales en base al código de excepción:

class Application_Model_DbTable_MyModel extends Zend_Db_Table_Abstract 

    public function insert($aData, $bIgnore = false) 
    { 

     try 
     { 
      $id = parent::insert($aData); 
     } 
     catch(Zend_Db_Statement_Mysqli_Exception $e) 
     { 
      // code 1062: Mysqli statement execute error : Duplicate entry 
      if($bIgnore && $e->getCode() == 1062) 
      { 
       // continue; 
      } 
      else 
      { 
       throw $e; 
      } 
     } 
     return !empty($id) ? $id : false; 
    } 
} 
+0

necesita renunciar a $ bIgnore debido a estándares estrictos: La declaración de Your_Model :: insert() debería ser compatible con Zend_Db_Table_Abstract :: insert (array $ data) en ... – bensiu

1

Mi solución:

/** 
* Perform an insert with the IGNORE word 
* 
* @param $data array 
* @return number the number of rows affected 
*/ 
public function insertIgnore(array $data) 
{ 
    // Start of query 
    $sql = sprintf("INSERT IGNORE INTO %s (", $this->getAdapter()->quoteIdentifier($this->info('name'))); 
    // Retrieve column identifiers 
    $identifiers = array_keys($data); 
    foreach ($identifiers as $key => $value) { 
     // Quote identifier 
     $identifiers[$key] = $this->getAdapter()->quoteIdentifier($value); 
    } 
    // Concat column identifiers 
    $sql .= implode(', ', $identifiers); 
    $sql .= ") VALUES ("; 
    foreach ($data as $key => $value) { 
     // Quote values 
     $data[$key] = $this->getAdapter()->quote($value); 
    } 
    // Concat values identifiers 
    $sql .= implode(', ', $data); 
    $sql .= ")"; 
    // Process the query 
    return $this->getAdapter()->query($sql)->rowCount(); 
} 
Cuestiones relacionadas