Mise à jour / insertion de base de données en masse à partir d'un fichier CSV

8

J'implémente une fonction d'importation de données spécifique à l'application d'une base de données à une autre.

J'ai un fichier CSV contenant disons 10000 lignes. Ces lignes doivent être insérées / mises à jour dans la base de données.

Il peut y avoir le cas, où quelques lignes peuvent se présenter dans la base de données, ce qui signifie qu'elles doivent être mises à jour. S'ils ne sont pas présents dans la base de données, ceux-ci doivent être insérés.

Une solution possible est que je peux lire une par une ligne, vérifier l'entrée dans la base de données et créer des requêtes d'insertion / mise à jour en conséquence. Mais ce processus peut prendre beaucoup de temps pour créer des requêtes de mise à jour / insertion et les exécuter dans la base de données. Parfois, mon fichier CSV peut contenir des millions d'enregistrements.

Existe-t-il un autre moyen plus rapide de réaliser cette fonctionnalité?


la source
Essayez de le traiter en plusieurs parties, sinon une grande lecture CSV d'un coup entraînera OutOfMemory!
@TheNewIdiot qui ne se produira pas si vous utilisez suffisamment de mémoire comme un serveur décent qui destine au moins 2 Go de RAM à la JVM. Cela dépendra également du type de données dans le fichier CSV et si le processus s'exécutera dans un seul processus ou à côté d'autres traités dans le serveur.
@ Luiggi Mendoza: Je suis d'accord avec vous. Nous avons suffisamment de mémoire pour traiter le gros fichier CSV en production.

Réponses:

7

Il existe une belle technologie disponible dans Oracle appelée Tables externes. Dans votre scénario, vous pouvez accéder à vos données externes en texte brut à l'aide de tables externes à partir de la base de données et mettre à jour vos données existantes dans la base de données avec des instructions SQL que vous aimez et auxquelles vous êtes habitué, par exemple INSERT, MERGEetc.

Dans la plupart des cas, l'utilisation des utilitaires fournis par Oracle est le meilleur moyen d'effectuer ETL. Et parce que votre question ressemble plus à une question administrative, je vous suggère de consulter mon précédent article sur DBA Stack Exchange "Mettre à jour la base de données Oracle à partir de CSV" .

MISE À JOUR: Cette approche fonctionne assez bien pour lire des données externes dans la base de données. En règle générale, vous définissez un format de données externe chaque fois que vous devez traiter le fichier en texte brut qui a un nouveau format. Une fois la table externe créée, vous pouvez l'interroger comme une vraie table de base de données. Chaque fois qu'il y a de nouvelles données à importer, il vous suffit de remplacer les fichiers sous-jacents à la volée sans avoir besoin de recréer des tables externes. Étant donné que la table externe peut être interrogée comme n'importe quelle autre table de base de données, vous pouvez écrire des instructions SQL pour remplir d'autres tables de base de données.

Les frais généraux liés à l'utilisation de tables externes sont généralement inférieurs à ceux d'autres techniques que vous implémenteriez manuellement, car cette technologie a été conçue en tenant compte des performances en tenant compte de l'architecture Oracle Database.

Yasir Arsanukaev
la source
Je suis d'accord que c'est l'une des solutions pour atteindre mon objectif. Comment cette approche peut-elle être adaptée au traitement CSV dynamique? Cela signifie que mon utilisateur d'application a la possibilité de télécharger plusieurs fichiers avec différents formats (dans ce cas, les contes externes doivent être créés à la volée). En outre, un fichier CSV peut contenir des données qui doivent être remplies dans plusieurs tables.
1

Je pense que vous devez utiliser SQL * Loader pour charger le fichier CSV dans une table temporaire, puis utiliser l'instruction MERGE pour insérer des données dans la table de travail.
SQL * Loader vous donnera plus de flexibilité que les tables externes et si l'on utilise une charge de chemin directe, c'est vraiment rapide. Et MERGE fera exactement ce dont vous avez besoin - INSÉRER de nouveaux enregistrements et METTRE À JOUR les enregistrements existants.
Quelques liens pour commencer:
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
la source
1
Lorsque vous chargez des données dans la base de données à l'aide de SQL Loader, le processus DBWR ou le processus SQL Loader écrivent des tampons dans les fichiers de données. Lorsque vous déplacez par la suite des données chargées vers d'autres tables, la base de données effectue une autre E / S. Je ne pense pas que ce travail supplémentaire puisse être justifié. Soit dit en passant, lorsque les tables externes utilisent le pilote ORACLE_LOADER, la syntaxe de définition du format de données d'entrée est la même que celle utilisée par l'utilitaire sqlldr car, essentiellement, il s'agit de la même technologie et peuvent donc être utilisées de manière interchangeable. Les tables externes dans ce scénario sont préférées car il n'est pas nécessaire de charger d'abord les données dans la base de données
Yasir Arsanukaev
Comme d'habitude, la réponse est "cela dépend" :). Dans notre cas, il est généralement plus pratique de charger d'abord dans la table temporaire et de traiter plus tard. Étant donné que la charge de chemin directe ne génère pas de rétablissement, les E / S supplémentaires sont presque imperceptibles parmi d'autres opérations. Dans d'autres cas, bien sûr, d'autres méthodes seront meilleures.
Mindaugas Riauba
0

PreparedStatements rendra la création des requêtes d'insertion ou de mise à jour très rapide. Vous devez en avoir trois PreparedStatements: un pour l'insertion, un pour la mise à jour et un pour vérifier si la ligne est déjà dans le tableau. Si vous êtes en mesure de conserver les mêmes ID entre le fichier CSV et la nouvelle base de données, la vérification pour voir si une ligne est présente à l'aide du champ primaryID devrait également être très rapide.

L'utilisation d'un insert en lot peut offrir un gain de performances. Au fur et à mesure que vous diffusez le fichier CSV, vous devez vérifier si la ligne est déjà là, puis effectuer une mise à jour ou ajouter la ligne à votre commande d'insertion par lots. Vous devriez vérifier cette question SO pour comparer la vitesse de ces deux approches.

Si cette importation de base de données doit être effectuée régulièrement et que les performances sont un problème en utilisant la méthode décrite ci-dessus, vous pouvez essayer de gérer la tâche avec plusieurs threads de travail. Utilisez autant de threads que de processeurs sur la machine exécutant ce code.

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

Chaque thread obtient sa propre connexion DB et au fur et à mesure que votre code parcourt le fichier, des lignes de CSV peuvent être transmises aux différents threads. C'est beaucoup plus compliqué, donc je ne ferais cela que si les exigences de performance m'y obligeaient.

Communauté
la source
Merci pour votre réponse. Encore une fois, cela nécessitera l'analyse des fichiers CSV et le remplissage des valeurs dans les instructions préparées. Avec l'approche «Tables externes», je vois que l'analyse de fichiers peut être déplacée du côté base de données où l'application n'a pas besoin de s'en soucier. De plus, j'utilise JPA avec Hibernate dans ma candidature. Je recherche l'option qui peut être la combinaison de JPA / Hibernate / Oracle qui facilite aucune analyse de fichiers, de bonnes performances, maintenable et flexible.