2012-07-02 25 views
6

Tengo el siguiente código (más o menos) para importar en cualquier lugar de 500.000 a 4.000.000 de filas:Records desaparecen en bucle DOP transacción mssql

$sSql = "Insert into table (a,b,c) VALUES(?,?,?)" 
$oSQLStmnt = $pdo->prepare($sSql); 
$oSQLStmnt->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM); 
if (!$oSQLStmnt) { 
    echo $pdo->errorInfo(); // Handle errors 
} 
$pdo->beginTransaction(); 
$iLineCounter = 1; 
while (($sLine = fgets ($oCSV, 8000)) !== FALSE) { 
     $aLine = explode('|', $sLine); //Fgetscsv did not work properly 
     if ($iLineCounter % 100 == 0) { 
      lo("Inserting row " . $iLineCounter); 
      $pdo->commit(); 
      sleep(0.15); 
      $pdo->beginTransaction(); 
     } 
     try { 
      $oSQLStmnt->execute($aLine); 
      $iSuccesulInserts++; 
     } 
     catch (exception $e) { 
      print_r($e); 
      $iFailedInserts++; 
     } 

     $iLineCounter++; 
} 
$pdo->commit(); 

Como se puede ver, realizar una confirmación cada 100 líneas , e incluso agregué algo de sueño. Solía ​​ejecutar el commit solo una vez cada 25,000 líneas, y no usé ningún sleep. Sin embargo, en un momento dado, descubrí que me faltaban registros. Empecé a jugar con estas configuraciones (suspensión y número de filas). De esta forma, reduje la cantidad de registros perdidos de 50,000 a alrededor de 100. ¡Pero aún me faltan registros! ¿A dónde van? Sé que el SQL está bien, porque de inmediato recibo errores cuando algo anda mal allí.

Pensé que podría apilar muchas inserciones durante una transacción? ¿Podría llamar a beginTransaction ser un problema?

ACTUALIZACIÓN:

La generosidad terminó y tuve que adjudicarlo. Gracias a todos por sus respuestas o comentarios. O consejos en realidad, ya que ninguno de ustedes respondió mi pregunta. No estaba pidiendo una solución alternativa, aunque sus sugerencias son muy apreciadas. La respuesta que se otorgó a la recompensa fue recibida porque estuvo más cerca de responder mi pregunta. Desafortunadamente, no funcionó.

Por el momento estoy usando la importación masiva CSV, que funciona bien, pero si alguien tiene otros consejos para solucionar este problema, por favor hágamelo saber. Como prefiero usar mi método original.

+0

Ejecutando el código sin comenzar la Transacción y apilando todas las consultas de inserción en una transacción resulta en la desaparición de aproximadamente 40,000 registros ... –

+0

Si repito este ciclo sin transacciones, funciona bien. No se pierden registros ... –

+0

El problema no está causado por PDO. Eso es seguro. –

Respuesta

1

¿Ha considerado utilizar Sprocs en lugar de insertar instrucciones? escribir CUALQUIER cantidad de registros secuencialmente, uno a la vez, es una pérdida de tiempo/energía ... simplemente no es tan rápido como debería ser.

¿Estás seguro de que no puedes usar BULK INSERT o XML para insertar varias filas a la vez?

+0

Eso es lo que estoy haciendo ahora como una solución alternativa. Pero creo que es horrible que los registros desaparezcan sin previo aviso ... –

+0

La importación masiva de CSV era la única forma confiable de hacerlo. –

3

Tuve este problema antes. Para mí, tuve que hacer un "SET NOCOUNT ON" antes de los INSERTS porque SQL Server intentaba devolverme "Se agregó una fila" para cada INSERT y la cola de mensajes estaba llena y simplemente se detuvo al insertar datos, ¡sin devolver ningún error!

Por lo tanto, definitivamente debe intentar hacer una "CONFIGURAR NOCOUNT ENCENDIDA" antes de los INSERTOS. Apuesto a que solucionará tu problema.

+0

¡Eso suena totalmente plausible! Voy a intentarlo hoy! –

+0

¿Antes de cada declaración de inserción o solo una vez? –

+0

Desafortunadamente no lo resuelve. '14: 57: 10 [119] | RESULTADO DE la tabla: Total de líneas: 466792Succesful: 466789 Fallido: 2 '-> 'seleccionar conteo (*) de la tabla' =' 441925' –

2

@Saratis,

¿Usted ha considerado la creación de un procedimiento almacenado simple que lleva a cabo la acción deseada utilizando una combinación? La fusión consumirá una sobrecarga considerable, sin embargo, siempre he sabido que es una forma muy confiable de sincronizar registros desde una fuente de datos 'maestra' a una fuente de datos dependiente.

Soy de la filosofía de que la base de datos debe controlar cómo se usan los datos, y el código debe controlar CUÁNDO la base de datos hace lo que hace. Lo que prefiero hacer es mantener todo lo que toque datos en un proceso almacenado, y llamar procs almacenados con código cuando ocurran ciertas condiciones/eventos. Sin embargo, su situación podría ser lo suficientemente única como para que esta no sea exactamente una mejor práctica.

El siguiente fragmento de código proviene de Microsoft como un ejemplo de cómo llevar a cabo una fusión:

MERGE Production.UnitMeasure AS target 
USING (SELECT @UnitMeasureCode, @Name) AS source (UnitMeasureCode, Name) 
ON (target.UnitMeasureCode = source.UnitMeasureCode) 
WHEN MATCHED THEN 
    UPDATE SET Name = source.Name 
WHEN NOT MATCHED THEN 
    INSERT (UnitMeasureCode, Name) 
    VALUES (source.UnitMeasureCode, source.Name) 
    OUTPUT deleted.*, $action, inserted.* INTO #MyTempTable; 

Aquí está el enlace a todo el artículo, que abarca una serie de escenarios diferentes: http://technet.microsoft.com/en-us/library/bb510625.aspx

Ahora, para obtener la información en el SQL Server desde un CSV, el siguiente enlace explica cómo se puede lograr utilizando la ruta del archivo como parte de la cláusula FROM y especificando el delimitador en una cláusula WITH.

Cubre BULK INSERT también, si eso puede funcionar mejor para usted, sin embargo, soy pariente de MERGE porque maneja INSERT para nuevos registros y ACTUALIZA los registros existentes. http://sqlserverpedia.com/blog/sql-server-bloggers/so-you-want-to-read-csv-files-huh/

FYI, BULK INSERT solo funciona si los archivos están ubicados en los mismos discos que la instancia de SQL Server. Es comprensible que mi empresa no me otorgue acceso a las unidades locales del servidor SQL, así que tendré que probar esto en casa esta noche para obtener un ejemplo de trabajo con el que trabajar.

+0

Esto es bueno, pero no creo que se aplique en la importación de un archivo CSV, ¿o estoy equivocado? –

+0

Disculpe, no pude ver en su publicación original que estaba importando de CSV. Este enlace podría ofrecer una solución. http://sqlserverpedia.com/blog/sql-server-bloggers/so-you-want-to-read-csv-files-huh/ Seleccione el CSV en una Expresión común de tabla y luego realice la fusión. Voy a actualizar mi respuesta para incluir este enlace también. – EastOfJupiter

3

Usa sleep() 0.15 segundos para retrasar la ejecución, sin embargo, pregunta: ¿Qué sucede si el INSERT toma más de 0.15 segundos? El script que se ejecutará y la tabla se pueden bloquear debido a la confirmación previa.

Luego pruebe un enfoque de múltiples INSERT en una sola ejecución en la base de datos. Intentar algo como esto:

INSERT INTO example (example_id, name, value, other_value)VALUES 
(100, 'Name 1', 'Value 1', 'Other 1'), (101, 'Name 2', 'Value 2', 'Other 2'), 
(102, 'Name 3', 'Value 3', 'Other 3'), (103, 'Name 4', 'Value 4', 'Other 4'); 

Para lograr esto, hacer:

$sql = ' INSERT INTO example (example_id, name, value, other_value)VALUES'; 
while (($sLine = fgets ($oCSV, 8000)) !== FALSE) { 
    // generate VALUES to INSERT in a $sql .= '(..., ..., ...),' 
} 

Y a continuación, ejecutar!