Actualización / inserción masiva de la base de datos del archivo CSV

8

Estoy implementando la función de importación de datos específicos de la aplicación de una base de datos a otra.

Tengo un archivo CSV que contiene digamos 10000 filas. Estas filas deben insertarse / actualizarse en la base de datos.

Puede haber un caso en el que un par de filas pueden presentarse en la base de datos, lo que significa que deben actualizarse. Si no está presente en la base de datos, es necesario insertarlos.

Una posible solución es que puedo leer una por una línea, verificar la entrada en la base de datos y generar consultas de inserción / actualización en consecuencia. Pero este proceso puede tomar mucho tiempo para crear consultas de actualización / inserción y ejecutarlas en la base de datos. Algunas veces mi archivo CSV puede tener millones de registros.

¿Hay alguna otra forma más rápida de lograr esta función?


fuente
¡Intente procesarlo en partes o de lo contrario se obtendrá una gran lectura de CSV de una vez OutOfMemory!
@TheNewIdiot eso no sucederá si se usa suficiente memoria como un servidor decente que destina al menos 2 GB de RAM a la JVM. También dependerá del tipo de datos en el archivo CSV y si el proceso se ejecutará en un solo proceso o al lado de otro procesado en el servidor.
@Luiggi Mendoza: estoy de acuerdo contigo. Tenemos suficiente memoria para procesar el gran archivo CSV en producción.

Respuestas:

7

Hay una buena tecnología disponible en Oracle llamada Tablas externas. En su situación, puede acceder a sus datos externos de texto sin formato usando Tablas externas desde la base de datos y actualizar sus datos existentes en la base de datos con declaraciones SQL que le encantan y a las que está acostumbrado, por ejemplo INSERT, MERGEetc.

En la mayoría de los casos, usar las utilidades suministradas por Oracle es la mejor manera de realizar ETL. Y debido a que su pregunta se parece más a una administrativa, le sugiero que consulte mi publicación anterior en DBA Stack Exchange "Actualizar la base de datos Oracle desde CSV" .

ACTUALIZACIÓN: Este enfoque funciona bastante bien para leer datos externos en la base de datos. Generalmente, usted define el formato de datos externos cada vez que necesita procesar el archivo de texto sin formato que tiene un nuevo formato. Una vez que se crea la tabla externa, puede consultarla como una tabla de base de datos real. Siempre que haya nuevos datos para importar, simplemente reemplace los archivos subyacentes sobre la marcha sin necesidad de volver a crear tablas externas. Dado que la tabla externa se puede consultar como cualquier otra tabla de base de datos, puede escribir instrucciones SQL para llenar otras tablas de base de datos.

La sobrecarga del uso de tablas externas generalmente es menor en comparación con otras técnicas que implementaría manualmente porque esta tecnología se diseñó teniendo en cuenta el rendimiento teniendo en cuenta la arquitectura de la base de datos Oracle.

Yasir Arsanukaev
fuente
Estoy de acuerdo en que esta es una de las soluciones para lograr mi objetivo. ¿Cómo este enfoque puede ser adecuado para el procesamiento dinámico de CSV? Significa que el usuario de mi aplicación tiene la oportunidad de cargar múltiples archivos con diferentes formatos (en este caso, los cuentos externos deben crearse sobre la marcha). Además, un archivo CSV puede contener datos que deben rellenarse en varias tablas.
1

Creo que debería usar SQL * Loader para cargar el archivo CSV en la tabla temporal y luego usar la declaración MERGE para insertar datos en la tabla de trabajo.
SQL * Loader le dará más flexibilidad que las tablas externas y si uno usa la carga de ruta directa, es realmente rápido. Y MERGE hará exactamente lo que necesita: INSERTAR nuevos registros y ACTUALIZAR los existentes.
Par de enlaces para comenzar:
http://docs.oracle.com/cd/B19306_01/server.102/b14215/ldr_concepts.htm
http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016 .htm

Mindaugas Riauba
fuente
1
Cuando carga datos en la base de datos utilizando SQL Loader, el proceso DBWR o el proceso SQL Loader escriben memorias intermedias en los archivos de datos. Cuando posteriormente mueve los datos cargados a otras tablas, la base de datos realiza otra E / S. No creo que este trabajo extra pueda justificarse. Por cierto, cuando las tablas externas utilizan el controlador ORACLE_LOADER, la sintaxis para definir el formato de datos de entrada es la misma que utiliza la utilidad sqlldr porque esencialmente son la misma tecnología y, por lo tanto, pueden usarse indistintamente. Se prefieren las tablas externas en este escenario, ya que no es necesario cargar primero los datos en la base de datos
Yasir Arsanukaev
Como respuesta habitual es "depende" :). En nuestro caso, generalmente es más conveniente cargar primero en la tabla temporal y procesar más tarde. Como la carga de ruta directa no genera rehacer, las E / S adicionales son casi imperceptibles entre otras operaciones. En otros casos, por supuesto, otros métodos serán mejores.
Mindaugas Riauba
0

PreparedStatements hará que la creación de consultas de inserción o actualización sea muy rápida. Debe tener tres PreparedStatements: uno para insertar, uno para actualizar y otro para verificar si la fila ya está en la tabla. Si puede mantener las ID iguales entre el archivo CSV y la nueva base de datos, entonces verificar si hay una fila usando el campo primaryID también debería ser muy rápido.

El uso de una inserción por lotes puede ofrecer una ganancia de rendimiento. A medida que transmite a través del archivo CSV, verificará si la fila ya está allí y luego realizará una actualización o agregará la fila a su comando de inserción por lotes. Debe verificar esta pregunta SO para la comparación de velocidad de estos dos enfoques.

Si la importación de esta base de datos es algo que debe hacerse regularmente y el rendimiento es un problema utilizando el método que describí anteriormente, puede intentar manejar la tarea con múltiples subprocesos de trabajo. Utilice tantos subprocesos como procesadores en la máquina que ejecuta este código.

  int nThreads = Runtime.getRuntime().availableProcessors();

Cada subproceso tiene su propia conexión de base de datos y, a medida que su código itera a través del archivo, se pueden pasar líneas de CSV a los distintos subprocesos. Esto es mucho más complicado, por lo que solo haría esto si los requisitos de rendimiento me obligan.

Comunidad
fuente
Gracias por su respuesta. Nuevamente, esto requerirá el análisis de archivos CSV y el llenado de valores en declaraciones preparadas. Con el enfoque de 'Tablas externas', veo que el análisis de archivos se puede mover al lado de la Base de datos donde la aplicación no necesita preocuparse por ello. Además, estoy usando JPA con Hibernate en mi aplicación. Estoy buscando la opción que puede ser la combinación de JPA / Hibernate / Oracle que facilita el análisis de archivos, buen rendimiento, mantenimiento y flexibilidad.