Migration : choix technique

Migration : choix technique - SQL/NoSQL - Programmation

Marsh Posté le 22-08-2007 à 10:59:10    

Bonjour,
 
je dois réaliser un utilitaire permettant de migrer les données d'une base de vers une autre.
Jusque là c'est un cas de migration simple, sauf que je dois tenir compte des cas suivants :
 
1°/ Les deux bases de données (source et destination) peuvent ne pas être disponible en même temps. En effet les données  
 
peuvent aller d'un serveur vers un autre, le serveur n'étant pas encore disponible. On doit pouvoir migrer les données  
 
depuis un serveur qui est sous windows 2003 server vers le même serveur qui sera formatté sous linux, par exemple.
 
2°/ La migration de ces données doit également prévoir la migration sqlserver <-> mysql
 
Mon problème ne concerne pas la réalisation de cet utilitaire en soit, mais plutôt un cchoix technique, explications :
 
- Les deux contraintes ci-dessus imposent d'office le fait de passer par des fichiers contenant les données, puisque les  
 
serveurs de base de données peuvent ne pas être disponibles simultanément, ou envore géographiquement, ce qui exclu  
 
l'utilisation de connecteur (odbc, etc...).
- Le choix de passer par ces fichiers semblent donc offrir la souplesse dont j'ai besoin pour la réalisation de ce petit  
 
outil et permet de couvrir toutes les contraintes fonctionnelles qui me sont imposées.
 
=> Deux solutions sont possibles :
a) passer par des fichiers à plat.
b) passer par des fichiers XML
 
La question du choix se pose : j'ai remarqué que beaucoup recommandent de passer par des fichiers à plat (type texte) pour  
 
contenir les données à migrer, l'outil de migration de SQLServer 2005 propose lui même de passer par des fichiers a plat,  
 
des fichiers excel et d'autre formats mais en aucun cas par des fichiers XML.
 
Premiere question : quel est, selon vous la meilleure solution et pourquoi ?
Deuxième question : pourquoi le XML n'est il jamais retenu dans les solutions de migration ?
 
Merci d'avance.
 
 :hello:

Reply

Marsh Posté le 22-08-2007 à 10:59:10   

Reply

Marsh Posté le 22-08-2007 à 15:13:24    

Il n'y a pas de meilleure solution.
 
Les fichiers "à plat", déjà, ça regroupe plusieurs types de fichiers.
1/ On va trouver les fichiers "record". Ces fichiers stockent les informations sous forme de chaînes de caractères de tailles fixes, collées les unes à la suite des autres.
Ce type de fichiers à pour avantages :
- Très facile à créer avec VB ou en Pascal par exemple
- Extrêment rapide à lire et écrire (y'a rien de plus rapide en fait, mise à part des données binaires brutes).
- Beaucoup d'outils savent les relire (Excel par exemple propose un assistant pour lire de tels fichiers).
Mais un gros inconvénient :
- Ils n'ont pas de structure annoncée. Ainsi, impossible de détecter automatiquement ce qu'il contient.
2/ Le CSV. C'est le type de fichiers plat le plus courant. Il s'agit de stocker les valeurs de chaque ligne de ta base de donnés dans une ligne du fichier CSV, les valeurs étant séparées par des virgules (ou autre séparateur) et les chaînes de caractères entre guillements (ou autre caractère d'échappement).
Ce type de fichiers a des avantages :
- Très facile à générer avec n'importe quel langage
- Il peut contenir une ligne d'entête qui permet de retrouver plus ou moins ce qu'il contient en lisant cette ligne (on a du moins le nom des colonnes)
Mais aussi des inconvénients :
- Assez lent
- Pose des problèmes avec les nombres et les chaînes de caractères, en fonction des caractères de séparations et d'échappement retenus
3/ Fichiers excel, ou autres logiciels tiers.
Quelques avantages certains :
- On peut très aisément automatiser leur création et injection dans un SGBD via des macros
- Ils sont directement lisibles
- Les données conservent leur type durant le transport
Mais des inconvénients :
- Très lents
- Il faut avoir le logiciel sur les deux machines
4/ Fichiers SQL
Avantages :
- La plupart des SGDB permettent de les générer automatiquement
- On peut les intégrer dans la destination sans le moindre développement
- On peut aisément modifier directement le code pour l'adapter à la destination
Inconvénients :
- Problèmes éventuels de charset
- Problèmes de non conformité des instructions et des séquences d'échappement entre les différents SBDG
- Fichier très volumineux
- Obligé de découper les fichiers en rondelles si on ne veut pas faire exploser le rollback segment
 
Reste le XML : Il est à la base prévu pour faire justement ce genre de choses : démartérialiser une base de données afin de transférer ses informations sous forme de fichiers de données.
Il a de gros avantages :
- Hiérachique et données typées : on retrouve donc immédiatement ce qu'on a dedans en relisant le fichier
- Aucun problème de jeu de caractères car il contient les informations d'encodage
- De nombreux outils permettent de travailler avec
- De nombreux SGBD savent les lire et écrire nativement (depuis quelques temps seulement)
Mais aussi des inconvénients :
- Perte immense de taille
- Malheureusement, obligé de faire du dev spécifique (y'a pas ou peu de formats prédéfinis... le plus générique serait de faire des DataSet avec .NET et les sérialiser en XML avec la méthode par défaut. Mais ça implique de travailler en .NET sous Linux, donc avec Mono et tout n'est pas porté...
 
En tout cas, le XML n'est pas à déconseiller. C'est par contre une solution aussi lourde que les autres, ce qui est bien domage.

Reply

Marsh Posté le 22-08-2007 à 15:53:52    

Merci pour la réponse MagicBuzz,
 
je récapitule :
 
1°/ Le fichier record : le fait qu'il ne soit pas structuré ne me pose pas de problème particulier dans la mesure où je compte stocker le schéma de la base dans le fichier. Donc potentiellement c'est une solution viable.
2°/ Le CSV : même optique, la classe que j'utilise pour gérer les données contenues dans les fichiers tiendra compte de ces caractères (mais je dois effectivement y faire attention).
3°/ Les fichiers Excel ne me semblent pas être une bonne solution dans la mesure où je dois prévoire les environnements de type UNIX et autres, de plus je ne me vois pas trop demander l'installation d'Excel sur un serveur pour les besoins de la migration.
4°/ Les fichiers SQL : il faudrait que je me renseigne pour générer ce fichier avec la console d'administration de sqlserver 2005. Et aussi pour m'assurer que le code est conforme à la fois de SQL server et de Mysql.
 
En ce qui concerne mes interrogations sur le XML tu viens de m'éclairer nettement et je t'en remercie, cependant je ne comprend pas bien ce que tu entends par "Perte immense de taille " et "y'a pas ou peu de formats prédéfinis". Pourrais tu m'éclairer sur ces deux points ?

Reply

Marsh Posté le 22-08-2007 à 16:52:09    

frere tuck a écrit :

En ce qui concerne mes interrogations sur le XML tu viens de m'éclairer nettement et je t'en remercie, cependant je ne comprend pas bien ce que tu entends par "Perte immense de taille " et "y'a pas ou peu de formats prédéfinis". Pourrais tu m'éclairer sur ces deux points ?


Exemple :
 
Tu peux avoir un fichier XML de ce genre :

Code :
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <personne>
  3.  <line>
  4.    <id>1</id>
  5.    <nom>Toto</nom>
  6.  </line>
  7.  <line>
  8.    <id>2</id>
  9.    <nom>Alfred</nom>
  10.  </line>
  11.  <line>
  12.    <id>3</id>
  13.    <nom>Tintin</nom>
  14.  </line>
  15. </personne>


 
Mais pour les mêmes infos, tu peux avoir :
 

Code :
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <dataset>
  3.  <tableshema>
  4.    <table>
  5.      <name>personne</name>
  6.      <columns>
  7.          <column name="id" type="numeric" size="18" withnull="no"/>
  8.          <column name="nom" type="varchar" size="50" withnull="yes"/>
  9.      </columns>
  10.    </table>
  11.  </tableshema>
  12.  <tabledata>
  13.    <personne id="1" nom="Toto"/>
  14.    <personne id="2" nom="Alfred"/>
  15.    <personne id="3" nom="Tintin"/>
  16.  </tabledata>
  17. </dataset>


 
Les deux contiennent les mêmes données, mais n'ont absolument pas la même structure.
Le premier ressemble à ce qu'on obtient en .NET lorsqu'on fait un .WriteXml() sur un objet DataTable, tandis que le second ressemble à ce qu'on obtient quand on lance la même méthode sur un objet DataSet.
 
Et ceci c'est que deux exemple. Il y a une infinité de possibilités pour un même énoncé et un même problème.
C'est pourquoi je dis que c'est pas trop standardisé.
Il existe des DTD permettant d'uniformiser les choses, mais elles sont rarement connues, et trop souvent trop spécialisées et complexes, ce qui les rend inaccessible pour une one shot.
 
Pour ce qui est de la volumétrie, tu remarqueras comparé à un simple fichier CSV :


1,"Toto"
2,"Alfred"
3,"Tintin"


 
Le XML rajoute un peu beaucoup de bordel autour...

Reply

Marsh Posté le 22-08-2007 à 17:40:03    

c'est pas vraiment de la migration mais de la réplication que tu veux faire nan?
Si je demande, c'est juste que recopier les données c'est pas potentiellement suffisant...
Je pense par exemple à des objets sequences qui pourraient être utilisées sur ta base ou ce genre de chose et qu'il conviendrait de "mettre à jour"...


Message édité par anapajari le 22-08-2007 à 17:40:36
Reply

Marsh Posté le 23-08-2007 à 09:36:28    

Je veux simplement pouvoir transférer mes données d'une base "test" sur un serveur sql server, vers une base "test" de schéma en tous points identiques.
Apres pour les objets sequences, il faut que je refasse des recherches je ne connais pas ou alors pas dans ces termes mais je te remercie de la suggestion.

Reply

Marsh Posté le 23-08-2007 à 09:46:02    

MagicBuzz a écrit :


Exemple :
 
Tu peux avoir un fichier XML de ce genre :

Code :
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <personne>
  3.  <line>
  4.    <id>1</id>
  5.    <nom>Toto</nom>
  6.  </line>
  7.  <line>
  8.    <id>2</id>
  9.    <nom>Alfred</nom>
  10.  </line>
  11.  <line>
  12.    <id>3</id>
  13.    <nom>Tintin</nom>
  14.  </line>
  15. </personne>


 
Mais pour les mêmes infos, tu peux avoir :
 

Code :
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <dataset>
  3.  <tableshema>
  4.    <table>
  5.      <name>personne</name>
  6.      <columns>
  7.          <column name="id" type="numeric" size="18" withnull="no"/>
  8.          <column name="nom" type="varchar" size="50" withnull="yes"/>
  9.      </columns>
  10.    </table>
  11.  </tableshema>
  12.  <tabledata>
  13.    <personne id="1" nom="Toto"/>
  14.    <personne id="2" nom="Alfred"/>
  15.    <personne id="3" nom="Tintin"/>
  16.  </tabledata>
  17. </dataset>


 
Les deux contiennent les mêmes données, mais n'ont absolument pas la même structure.
Le premier ressemble à ce qu'on obtient en .NET lorsqu'on fait un .WriteXml() sur un objet DataTable, tandis que le second ressemble à ce qu'on obtient quand on lance la même méthode sur un objet DataSet.
 
Et ceci c'est que deux exemple. Il y a une infinité de possibilités pour un même énoncé et un même problème.
C'est pourquoi je dis que c'est pas trop standardisé.
Il existe des DTD permettant d'uniformiser les choses, mais elles sont rarement connues, et trop souvent trop spécialisées et complexes, ce qui les rend inaccessible pour une one shot.
 
Pour ce qui est de la volumétrie, tu remarqueras comparé à un simple fichier CSV :


1,"Toto"
2,"Alfred"
3,"Tintin"


 
Le XML rajoute un peu beaucoup de bordel autour...


 
Donc en fait le problème avec le XML viendrai de la standardisation, ou plutôt de la non-standardisation d'une DTD commune à toutes les méthodes d'export si je ccomprends bien... la volumétrie est un autre problème mais si ça peut garantir l'utilisation d'un parseur ça se justifie non ?
 
Dans tous les cas, je trouve assez remarquable le fait que cette méthode ne soit que rarement conseillée. En réalité ce qui me met la puce à l'oreille c'est que l'export des données au format XML n'est pas possible par l'assistant dans sql server et que tous les autre formats d'export sont prise en charge.

Reply

Marsh Posté le 23-08-2007 à 09:46:32    

Oui ça j'ai bien compris que tu as besoin de recopier... Je vais reformuler la question, ta base de recopie a-t-elle pour but :
- de contenir une sauvegarde de la BDD,
- d'être utilisable par une application.
 
C'est pas du tout la même problèmatique car dans le 1er cas, il suffit de s'attacher à bien recopier les données alors que dans le deuxième il faut également les informations de structure de ta base (PK, FK, sequences, proc. stockées) et d'une base à une autre, attention les surprises ;)

Reply

Marsh Posté le 23-08-2007 à 10:15:09    

anapajari a écrit :

Oui ça j'ai bien compris que tu as besoin de recopier... Je vais reformuler la question, ta base de recopie a-t-elle pour but :
- de contenir une sauvegarde de la BDD,
- d'être utilisable par une application.
 
C'est pas du tout la même problèmatique car dans le 1er cas, il suffit de s'attacher à bien recopier les données alors que dans le deuxième il faut également les informations de structure de ta base (PK, FK, sequences, proc. stockées) et d'une base à une autre, attention les surprises ;)


 
Justement nous sommes dans le cas ou une application va devoir se servir de cette base de données.
 
En fait j'ai une application qui se sert d'une base de données sur un serveur sql server, et cette même application va devoir se servir de la base migrée sous un serveur mysql, par exemple.


Message édité par frere tuck le 23-08-2007 à 10:15:21
Reply

Marsh Posté le 23-08-2007 à 10:28:52    

D'un autre côté, tu peux extraire les données en XML directement en faisant une requête, donc ça sert pas à grand chose de faire un assistant pour ça...

Reply

Marsh Posté le 23-08-2007 à 10:28:52   

Reply

Marsh Posté le 23-08-2007 à 12:17:22    

Effectivement, mais les utilisateurs de l'application n'ont pas les connaissances pour effectuer ce genre de manipulation, d'ou le fait que je sois obligé de les guider dans cette démarche.

Reply

Marsh Posté le 25-08-2007 à 00:03:38    

Au final je pense que je vais passer par des fichiers à plat (csv) :
 
les + pour moi :
- c'est suffisamment souple pour couvrir l'ensemble de ce que je vais rencontrer comme environnement de migration
- techniquement c'est simple à générer et je peux stocker le schéma dans le fichier
- je peux gérer les caractères d'échappement moi même
 
Au niveau des inconvénient la lenteur n'est pas un problème dans la mesure où ça sera une opération ponctuelle.
 
Donc faute de certitudes je vais prendre cette solution, mais je reste dubitatif sur le cas du XML : c'est un format fait pour ce genre d'opération et pourtant la seule chose que l'on peut me dire sur les migrations de bases de données, c'est que toutes les applications utilisent les fichiers à plat.
Par contre pas moyen d'en trouver la raison pour le moment.
 
En tout cas merci pour les réponses, je vais poursuivre mes recherches et je posterai la finalité de la chose dans ce topic.

Reply

Marsh Posté le 25-08-2007 à 00:49:14    

(ceci dit, un fichier XML est un fichier plat comme un autre...)

Reply

Marsh Posté le 27-08-2007 à 09:48:12    

Ce que je voulais dire par là, c'est que le XML était rarement utilisé.
Je continue à faire quelques recherches quand même pour assurer le coup.

Reply

Marsh Posté le 28-08-2007 à 14:31:45    

Plop.
 
Voici un petit exemple, puisque je suis actuellement en train de travailler sur une appli qui fait elle aussi de la réplication de données.
 
En fait, dans mon cas il s'agit d'extraire des données quotidiennement depuis une base centrale, puis les envoyer à des clients par serveur FTP interposé afin de maintenir des bases locales à jour.
 
Histoire de ne pas me faire chier, j'utilise tout bêtement les fonctionnalités de sérialisation des DataSet de .NET (comment c'est de la balle :love:)
 
Extraction : (extrait, la classe qui nous intéresse : extraction des données, mise sous forme de dataset, sérialisation dans un flux mémoire, puis compression)

Code :
  1. using System;
  2. using System.Data;
  3. using Oracle.DataAccess.Client;
  4. using System.IO;
  5. using System.IO.Compression;
  6. using System.Text;
  7.  
  8. namespace DatabaseMaker
  9. {
  10.    /// <summary>
  11.    /// Outil d'extraction de différenciels de la base Générix
  12.    /// </summary>
  13.    public class DatabaseExtractor
  14.    {
  15.        OracleConnection cnx;
  16.        string datdeb;
  17.        string datfin;
  18.        decimal[] aCodsoc;
  19.        string[] aCodlan;
  20.  
  21.        string outPath;
  22.  
  23.        DataSet ds;
  24.  
  25.        /// <summary>
  26.        /// Instancie l'outil d'extraction de différenciels de la base Générix
  27.        /// </summary>
  28.        /// <param name="OpenedCnx">Connection active vers la base Générix</param>
  29.        /// <param name="OutPath">Répertoire de sortie pour les fichiers compressés</param>
  30.        /// <param name="Codsoc">Liste des code sociétés à exporter</param>
  31.        public DatabaseExtractor(OracleConnection OpenedCnx, string OutPath, decimal[] Codsoc)
  32.        {
  33.            cnx = OpenedCnx;
  34.            outPath = OutPath;
  35.            aCodsoc = Codsoc;
  36.        }
  37.  
  38.        /// <summary>
  39.        /// Extraction du différenciel sur une période donnée
  40.        /// Un fichier compressé est généré par société
  41.        /// Et chaque fichier compressé contient l'ensemble des éléments exportés séparés en fichiers
  42.        /// contenant à chaque fois les données d'une table pour une langue donnée
  43.        /// </summary>
  44.        /// <param name="Datdeb">Date de début de l'extraction. Laisser vide pour exporter depuis le début.</param>
  45.        /// <param name="Datfin">Date de fin. Obligatoirement présent. La date du jour en général.</param>
  46.        public void Extract(string Datdeb, string Datfin)
  47.        {
  48.            this.datdeb = Datdeb;
  49.            this.datfin = Datfin;
  50.  
  51.            // Extraction pour chaque société
  52.            for (int i = 0, cpti = aCodsoc.Length; i < cpti; i++)
  53.            {
  54.                // Création du DataSet
  55.                ds = new DataSet();
  56.  
  57.                // Extraction des éléments de base
  58.                ExtractFamilies(aCodsoc[i]);
  59.                ExtractCritera(aCodsoc[i]);
  60.  
  61.                // Chargement des langues
  62.                LoadCodlan(aCodsoc[i]);
  63.  
  64.                // Extraction des libellés
  65.                ExtractFamiliesLabels(aCodsoc[i]);
  66.                ExtractCriteriaLabels(aCodsoc[i]);
  67.  
  68.                if (ds.Tables.Count > 0)
  69.                {
  70.                    // Flux en entrée
  71.                    MemoryStream ms = new MemoryStream();
  72.                    ds.WriteXml(ms, XmlWriteMode.WriteSchema);
  73.                    ms.Position = 0;
  74.  
  75.                    // Compression du fichier
  76.                    byte[] buffer = new byte[4096];
  77.                    FileStream dest = new FileStream(string.Format("{0}\\SOC{1}_DEB{2}_FIN{3}.gz", outPath, aCodsoc[i], datdeb, datfin), FileMode.Create, FileAccess.Write, FileShare.None);
  78.                    GZipStream gz = new GZipStream(dest, CompressionMode.Compress, false);
  79.                    while (true)
  80.                    {
  81.                        int bytes_read = ms.Read(buffer, 0, buffer.Length);
  82.                        if (bytes_read == 0)
  83.                        {
  84.                            break;
  85.                        }
  86.                        gz.Write(buffer, 0, bytes_read);
  87.                    }
  88.                    gz.Close();
  89.                    ms.Close();
  90.                }
  91.            }
  92.        }
  93.  
  94.        /// <summary>
  95.        /// Chargement des code langue pour une société donnée
  96.        /// </summary>
  97.        /// <param name="codsoc">Société à charger</param>
  98.        private void LoadCodlan(decimal codsoc)
  99.        {
  100.            OracleCommand cmd = this.cnx.CreateCommand();
  101.            cmd.CommandText = "select cletbl codlan from tbl where codsoc = :codsoc and codtbl = 'lan'";
  102.            cmd.CommandType = CommandType.Text;
  103.  
  104.            OracleParameter p1 = cmd.CreateParameter();
  105.            p1.Direction = ParameterDirection.Input;
  106.            p1.OracleDbType = OracleDbType.Decimal;
  107.            p1.ParameterName = "codsoc";
  108.            p1.Size = 38;
  109.            p1.Value = codsoc;
  110.            cmd.Parameters.Add(p1);
  111.  
  112.  
  113.            DataTable dt = new DataTable();
  114.            OracleDataAdapter da = new OracleDataAdapter(cmd);
  115.            da.Fill(dt);
  116.  
  117.            int cpt = dt.Rows.Count;
  118.            aCodlan = new string[cpt];
  119.  
  120.            for (int i = 0; i < cpt; i++)
  121.            {
  122.                aCodlan[i] = (string)dt.Rows[i]["codlan"];
  123.            }
  124.        }
  125.  
  126.        #region Données de base
  127.  
  128.        /// <summary>
  129.        /// Extraction des familles biblo (premier niveau)
  130.        /// </summary>
  131.        /// <param name="codsoc">Société à extraire</param>
  132.        private void ExtractFamilies(decimal codsoc)
  133.        {
  134.            OracleCommand cmd = this.cnx.CreateCommand();
  135.            cmd.BindByName = true;
  136.            cmd.CommandText = "select codsoc, codmdl codfam from mdl where codsoc = :codsoc";
  137.            cmd.CommandType = CommandType.Text;
  138.  
  139.            OracleParameter p1 = cmd.CreateParameter();
  140.            p1.Direction = ParameterDirection.Input;
  141.            p1.OracleDbType = OracleDbType.Decimal;
  142.            p1.ParameterName = "codsoc";
  143.            p1.Size = 38;
  144.            p1.Value = codsoc;
  145.            cmd.Parameters.Add(p1);
  146.  
  147.            DataTable dt = new DataTable("fam" );
  148.            OracleDataAdapter da = new OracleDataAdapter(cmd);
  149.            da.Fill(dt);
  150.  
  151.            if (dt.Rows.Count > 0)
  152.            {
  153.                ds.Tables.Add(dt);
  154.            }
  155.        }
  156.  
  157.        /// <summary>
  158.        /// Extraction des critères biblo
  159.        /// </summary>
  160.        /// <param name="codsoc">Société à extraire</param>
  161.        private void ExtractCritera(decimal codsoc)
  162.        {
  163.            OracleCommand cmd = this.cnx.CreateCommand();
  164.            cmd.BindByName = true;
  165.            cmd.CommandText = "select distinct codsoc, indzon codcri from zodm where codsoc = :codsoc";
  166.            cmd.CommandType = CommandType.Text;
  167.  
  168.            OracleParameter p1 = cmd.CreateParameter();
  169.            p1.Direction = ParameterDirection.Input;
  170.            p1.OracleDbType = OracleDbType.Decimal;
  171.            p1.ParameterName = "codsoc";
  172.            p1.Size = 38;
  173.            p1.Value = codsoc;
  174.            cmd.Parameters.Add(p1);
  175.  
  176.            DataTable dt = new DataTable("cri" );
  177.            OracleDataAdapter da = new OracleDataAdapter(cmd);
  178.            da.Fill(dt);
  179.  
  180.            if (dt.Rows.Count > 0)
  181.            {
  182.                ds.Tables.Add(dt);
  183.            }
  184.        }
  185.  
  186.        #endregion
  187.  
  188.        # region Libellés
  189.  
  190.        /// <summary>
  191.        /// Extraction des libellés des familles biblo
  192.        /// </summary>
  193.        /// <param name="codsoc">Société à extraire</param>
  194.        private void ExtractFamiliesLabels(decimal codsoc)
  195.        {
  196.            OracleCommand cmd = this.cnx.CreateCommand();
  197.            cmd.BindByName = true;
  198.            cmd.CommandType = CommandType.Text;
  199.  
  200.            string tmpCommandText = "select mdl.codsoc, tbl.cletbl codlan, mdl.codmdl codfam, mdl.libmdl libfam from mdl inner join tbl on tbl.codsoc = mdl.codsoc and tbl.codtbl = 'lan' and tbl.cletbl in ({0}) where mdl.codsoc = :codsoc";
  201.            StringBuilder lanlist = new StringBuilder(":codlan0" );
  202.  
  203.            OracleParameter pLan = cmd.CreateParameter();
  204.            pLan.Direction = ParameterDirection.Input;
  205.            pLan.OracleDbType = OracleDbType.Varchar2;
  206.            pLan.ParameterName = "codlan0";
  207.            pLan.Size = 3;
  208.            pLan.Value = aCodlan[0];
  209.            cmd.Parameters.Add(pLan);
  210.  
  211.            for (int i = 1, cpt = aCodlan.Length; i < cpt; i++)
  212.            {
  213.                lanlist.Append(", :codlan" );
  214.                lanlist.Append(i);
  215.  
  216.                OracleParameter p = cmd.CreateParameter();
  217.                p.Direction = ParameterDirection.Input;
  218.                p.OracleDbType = OracleDbType.Varchar2;
  219.                p.ParameterName = string.Format("codlan{0}", i);
  220.                p.Size = 3;
  221.                p.Value = aCodlan[i];
  222.                cmd.Parameters.Add(p);
  223.            }
  224.            cmd.CommandText = string.Format(tmpCommandText, lanlist.ToString());
  225.  
  226.            OracleParameter p1 = cmd.CreateParameter();
  227.            p1.Direction = ParameterDirection.Input;
  228.            p1.OracleDbType = OracleDbType.Decimal;
  229.            p1.ParameterName = "codsoc";
  230.            p1.Size = 38;
  231.            p1.Value = codsoc;
  232.            cmd.Parameters.Add(p1);
  233.  
  234.            DataTable dt = new DataTable("lblfam" );
  235.            OracleDataAdapter da = new OracleDataAdapter(cmd);
  236.            da.Fill(dt);
  237.  
  238.            if (dt.Rows.Count > 0)
  239.            {
  240.                ds.Tables.Add(dt);
  241.            }
  242.        }
  243.  
  244.        /// <summary>
  245.        /// Extraction des libellés des critères biblo
  246.        /// </summary>
  247.        /// <param name="codsoc">Société à extraire</param>
  248.        /// <param name="codlan">Langue à extraire</param>
  249.        private void ExtractCriteriaLabels(decimal codsoc)
  250.        {
  251.            OracleCommand cmd = this.cnx.CreateCommand();
  252.            cmd.BindByName = true;
  253.            string tmpCommandText = "select distinct zodm.codsoc, nvl(zonlan.codlan, lan.cletbl) codlan, zodm.indzon codcri, nvl(zonlan.lib1, zon.lib1) libcri " +
  254.                                "from zodm " +
  255.                                "inner join tbl lan on lan.codsoc = zodm.codsoc and lan.codtbl = 'lan' and lan.cletbl in ({0}) " +
  256.                                "inner join tbl zon on zon.codsoc = zodm.codsoc and zon.codtbl = 'zon' and zon.cletbl = zodm.indzon " +
  257.                                "left outer join tbllan zonlan on zonlan.codsoc = zon.codsoc and zonlan.codtbl = 'zon' and zonlan.cletbl = zon.cletbl and zonlan.codlan = lan.cletbl " +
  258.                                "where zodm.codsoc = :codsoc";
  259.            cmd.CommandType = CommandType.Text;
  260.  
  261.            StringBuilder lanlist = new StringBuilder(":codlan0" );
  262.  
  263.            OracleParameter pLan = cmd.CreateParameter();
  264.            pLan.Direction = ParameterDirection.Input;
  265.            pLan.OracleDbType = OracleDbType.Varchar2;
  266.            pLan.ParameterName = "codlan0";
  267.            pLan.Size = 3;
  268.            pLan.Value = aCodlan[0];
  269.            cmd.Parameters.Add(pLan);
  270.  
  271.            for (int i = 1, cpt = aCodlan.Length; i < cpt; i++)
  272.            {
  273.                lanlist.Append(", :codlan" );
  274.                lanlist.Append(i);
  275.  
  276.                OracleParameter p = cmd.CreateParameter();
  277.                p.Direction = ParameterDirection.Input;
  278.                p.OracleDbType = OracleDbType.Varchar2;
  279.                p.ParameterName = string.Format("codlan{0}", i);
  280.                p.Size = 3;
  281.                p.Value = aCodlan[i];
  282.                cmd.Parameters.Add(p);
  283.            }
  284.            cmd.CommandText = string.Format(tmpCommandText, lanlist.ToString());
  285.  
  286.            OracleParameter p1 = cmd.CreateParameter();
  287.            p1.Direction = ParameterDirection.Input;
  288.            p1.OracleDbType = OracleDbType.Decimal;
  289.            p1.ParameterName = "codsoc";
  290.            p1.Size = 38;
  291.            p1.Value = codsoc;
  292.            cmd.Parameters.Add(p1);
  293.  
  294.            DataTable dt = new DataTable("lblcri" );
  295.            OracleDataAdapter da = new OracleDataAdapter(cmd);
  296.            da.Fill(dt);
  297.  
  298.            if (dt.Rows.Count > 0)
  299.            {
  300.                ds.Tables.Add(dt);
  301.            }
  302.        }
  303.  
  304.        #endregion
  305.    }
  306. }


 
Ensuite, la récupération :

Code :
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Data.SqlClient;
  5. using System.IO;
  6. using System.IO.Compression;
  7. using System.Net;
  8. using System.Windows.Forms;
  9. using System.Xml;
  10.  
  11. namespace CatalogueOnlineClient
  12. {
  13.    public partial class MajAuto : Form
  14.    {
  15.        Parameters p;
  16.        SqlConnection cnx;
  17.        SocInfo[] s;
  18.  
  19.        public MajAuto(Parameters p, SocInfo[] s, SqlConnection cnx)
  20.        {
  21.            InitializeComponent();
  22.            this.p = p;
  23.            this.s = s;
  24.            this.cnx = cnx;
  25.  
  26.            SqlCommand cmd = cnx.CreateCommand();
  27.            cmd.CommandType = CommandType.Text;
  28.            cmd.CommandText = "delete soc";
  29.            cmd.ExecuteNonQuery();
  30.            cmd.CommandText = "delete lan";
  31.            cmd.ExecuteNonQuery();
  32.  
  33.            SqlParameter pCodsoc = cmd.CreateParameter();
  34.            pCodsoc.Direction = ParameterDirection.Input;
  35.            pCodsoc.ParameterName = "codsoc";
  36.            pCodsoc.Size = 18;
  37.            pCodsoc.SqlDbType = SqlDbType.Decimal;
  38.  
  39.            SqlParameter pLibsoc = cmd.CreateParameter();
  40.            pLibsoc.Direction = ParameterDirection.Input;
  41.            pLibsoc.ParameterName = "libsoc";
  42.            pLibsoc.Size = 50;
  43.            pLibsoc.SqlDbType = SqlDbType.VarChar;
  44.  
  45.            SqlParameter pCodlan = cmd.CreateParameter();
  46.            pCodlan.Direction = ParameterDirection.Input;
  47.            pCodlan.ParameterName = "codlan";
  48.            pCodlan.Size = 3;
  49.            pCodlan.SqlDbType = SqlDbType.VarChar;
  50.  
  51.            SqlParameter pLiblan = cmd.CreateParameter();
  52.            pLiblan.Direction = ParameterDirection.Input;
  53.            pLiblan.ParameterName = "liblan";
  54.            pLiblan.Size = 50;
  55.            pLiblan.SqlDbType = SqlDbType.VarChar;
  56.  
  57.            cmd.Parameters.Add(pCodsoc);
  58.  
  59.            for (int i = 0, cpti = s.Length; i < cpti; i++)
  60.            {
  61.                cmd.Parameters.Add(pLibsoc);
  62.  
  63.                cmd.CommandText = "insert into soc (codsoc, libsoc) values (@codsoc, @libsoc)";
  64.                pCodsoc.Value = s[i].codsoc;
  65.                pLibsoc.Value = s[i].libsoc;
  66.                cmd.ExecuteNonQuery();
  67.                
  68.                cmd.Parameters.Remove(pLibsoc);
  69.  
  70.                cmd.Parameters.Add(pCodlan);
  71.                cmd.Parameters.Add(pLiblan);
  72.  
  73.                cmd.CommandText = "insert into lan (codsoc, codlan, liblan) values (@codsoc, @codlan, @liblan)";
  74.                for (int j = 0, cptj = s[i].laninfo.Length; j < cptj; j++)
  75.                {
  76.                    pCodlan.Value = s[i].laninfo[j].codlan;
  77.                    pLiblan.Value = s[i].laninfo[j].liblan;
  78.                    cmd.ExecuteNonQuery();
  79.                }
  80.  
  81.                cmd.Parameters.Remove(pCodlan);
  82.                cmd.Parameters.Remove(pLiblan);
  83.            }
  84.        }
  85.  
  86.        private void MajAuto_Load(object sender, EventArgs e)
  87.        {
  88.            this.Visible = true;
  89.            this.Refresh();
  90.            string lastUpdate = p["LAST"];
  91.  
  92.            WebClient wc = new WebClient();
  93.            XmlDocument dom = new XmlDocument();
  94.            dom.LoadXml(wc.DownloadString(string.Format("http://192.168.0.2/test.php?deb={0}", lastUpdate)));
  95.            progressBar1.Maximum = dom.DocumentElement.ChildNodes.Count - 1;
  96.            progressBar2.Maximum = dom.DocumentElement.ChildNodes.Count - 1;
  97.  
  98.            foreach (XmlElement el in dom.DocumentElement.ChildNodes)
  99.            {
  100.                wc.DownloadFile(string.Format("http://192.168.0.2/{0}", el.InnerText), string.Format("./in/{0}", el.InnerText));
  101.                progressBar1.PerformStep();
  102.            }
  103.  
  104.            foreach (string file in Directory.GetFiles("in", "*.gz" ))
  105.            {
  106.                // Décompresser le gz dans un ms, puis passer directement dans un ds
  107.                FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
  108.                MemoryStream ms = new MemoryStream();
  109.                GZipStream gz = new GZipStream(fs, CompressionMode.Decompress, false);
  110.                byte[] buffer = new byte[4096];
  111.                while (true)
  112.                {
  113.                    int read_bytes = gz.Read(buffer, 0, buffer.Length);
  114.                    if (read_bytes == 0)
  115.                    {
  116.                        break;
  117.                    }
  118.                    ms.Write(buffer, 0, read_bytes);
  119.                }
  120.                gz.Close();
  121.                fs.Close();
  122.                File.Delete(file);
  123.                DataSet ds = new DataSet();
  124.                ms.Position = 0;
  125.                ds.ReadXml(ms, XmlReadMode.ReadSchema);
  126.  
  127.                string dir = file.Substring(0, file.LastIndexOf('.'));
  128.                string tmpCodsoc = file.Substring(file.LastIndexOf("\\SOC" ) + 4);
  129.                decimal codsoc = decimal.Parse(tmpCodsoc.Substring(0, tmpCodsoc.IndexOf('_')));
  130.  
  131.                foreach (DataTable dt in ds.Tables)
  132.                {
  133.                    switch (dt.TableName)
  134.                    {
  135.                        // Données de base
  136.                        case "fam":
  137.                            UpdateFam(codsoc, dt);
  138.                            break;
  139.                        case "cri":
  140.                            UpdateCri(codsoc, dt);
  141.  
  142.                        // Libellés
  143.                        case "lblfam":
  144.                            UpdateLblFam(codsoc, dt);
  145.                            break;
  146.                        case "lblcri":
  147.                            UpdateLblCri(codsoc, dt);
  148.  
  149.                        // Ca chie des bulles
  150.                        default:
  151.                            throw new Exception(string.Format("Table \"{0}\" non reconnue dans le fichier \"{1}\" !", dt.TableName, file))
  152.                            break;
  153.                    }
  154.                }
  155.                progressBar2.PerformStep();
  156.            }
  157.            this.Close();
  158.        }
  159.  
  160.        #region Données de base
  161.        private void UpdateFam(decimal codsoc, DataTable dt)
  162.        {
  163.            SqlCommand cmd = cnx.CreateCommand();
  164.            cmd.CommandType = CommandType.Text;
  165.            cmd.CommandText = "delete fam where codsoc = @codsoc";
  166.  
  167.            SqlParameter pCodsoc = cmd.CreateParameter();
  168.            pCodsoc.Direction = ParameterDirection.Input;
  169.            pCodsoc.ParameterName = "codsoc";
  170.            pCodsoc.Size = 18;
  171.            pCodsoc.SqlDbType = SqlDbType.Decimal;
  172.            cmd.Parameters.Add(pCodsoc);
  173.  
  174.            pCodsoc.Value = codsoc;
  175.            cmd.ExecuteNonQuery();
  176.  
  177.            cmd.CommandText = "insert into fam (codsoc, codfam) values (@codsoc, @codfam)";
  178.            SqlParameter pCodfam = cmd.CreateParameter();
  179.            pCodfam.Direction = ParameterDirection.Input;
  180.            pCodfam.ParameterName = "codfam";
  181.            pCodfam.Size = 6;
  182.            pCodfam.SqlDbType = SqlDbType.VarChar;
  183.            cmd.Parameters.Add(pCodfam);
  184.  
  185.            foreach (DataRow dr in dt.Rows)
  186.            {
  187.                pCodfam.Value = (string)dr["CODFAM"];
  188.                cmd.ExecuteNonQuery();
  189.            }
  190.        }
  191.  
  192.        private void UpdateCri(decimal codsoc, DataTable dt)
  193.        {
  194.            SqlCommand cmd = cnx.CreateCommand();
  195.            cmd.CommandType = CommandType.Text;
  196.            cmd.CommandText = "delete cri where codsoc = @codsoc";
  197.  
  198.            SqlParameter pCodsoc = cmd.CreateParameter();
  199.            pCodsoc.Direction = ParameterDirection.Input;
  200.            pCodsoc.ParameterName = "codsoc";
  201.            pCodsoc.Size = 18;
  202.            pCodsoc.SqlDbType = SqlDbType.Decimal;
  203.            cmd.Parameters.Add(pCodsoc);
  204.  
  205.            pCodsoc.Value = codsoc;
  206.            cmd.ExecuteNonQuery();
  207.  
  208.            cmd.CommandText = "insert into cri (codsoc, codcri) values (@codsoc, @codcri)";
  209.            SqlParameter pCodfam = cmd.CreateParameter();
  210.            pCodfam.Direction = ParameterDirection.Input;
  211.            pCodfam.ParameterName = "codcri";
  212.            pCodfam.Size = 6;
  213.            pCodfam.SqlDbType = SqlDbType.VarChar;
  214.            cmd.Parameters.Add(pCodfam);
  215.  
  216.            foreach (DataRow dr in dt.Rows)
  217.            {
  218.                pCodfam.Value = (string)dr["CODCRI"];
  219.                cmd.ExecuteNonQuery();
  220.            }
  221.        }
  222.        #endregion
  223.  
  224.        #region Libellés
  225.        private void UpdateLblFam(decimal codsoc, DataTable dt)
  226.        {
  227.            SqlCommand cmd = cnx.CreateCommand();
  228.            cmd.CommandType = CommandType.Text;
  229.            cmd.CommandText = "delete lbl where codsoc = @codsoc and codtbl = 'fam'";
  230.  
  231.            SqlParameter pCodsoc = cmd.CreateParameter();
  232.            pCodsoc.Direction = ParameterDirection.Input;
  233.            pCodsoc.ParameterName = "codsoc";
  234.            pCodsoc.Size = 18;
  235.            pCodsoc.SqlDbType = SqlDbType.Decimal;
  236.            cmd.Parameters.Add(pCodsoc);
  237.  
  238.            pCodsoc.Value = codsoc;
  239.            cmd.ExecuteNonQuery();
  240.  
  241.            cmd.CommandText = "insert into lbl (codsoc, codtbl, sigti1, sigti2, codlan, label) values (@codsoc, 'fam', @codfam, '', @codlan, @label)";
  242.            SqlParameter pCodfam = cmd.CreateParameter();
  243.            pCodfam.Direction = ParameterDirection.Input;
  244.            pCodfam.ParameterName = "codfam";
  245.            pCodfam.Size = 6;
  246.            pCodfam.SqlDbType = SqlDbType.VarChar;
  247.            cmd.Parameters.Add(pCodfam);
  248.            SqlParameter pCodlan = cmd.CreateParameter();
  249.            pCodlan.Direction = ParameterDirection.Input;
  250.            pCodlan.ParameterName = "codlan";
  251.            pCodlan.Size = 3;
  252.            pCodlan.SqlDbType = SqlDbType.VarChar;
  253.            cmd.Parameters.Add(pCodlan);
  254.            SqlParameter pLabel = cmd.CreateParameter();
  255.            pLabel.Direction = ParameterDirection.Input;
  256.            pLabel.ParameterName = "label";
  257.            pLabel.Size = 50;
  258.            pLabel.SqlDbType = SqlDbType.VarChar;
  259.            cmd.Parameters.Add(pLabel);
  260.  
  261.            foreach (DataRow dr in dt.Rows)
  262.            {
  263.                pCodfam.Value = (string)dr["CODFAM"];
  264.                pCodlan.Value = (string)dr["CODLAN"];
  265.                pLabel.Value = (string)dr["LIBFAM"];
  266.                cmd.ExecuteNonQuery();
  267.            }
  268.        }
  269.  
  270.        private void UpdateLblCri(decimal codsoc, DataTable dt)
  271.        {
  272.            SqlCommand cmd = cnx.CreateCommand();
  273.            cmd.CommandType = CommandType.Text;
  274.            cmd.CommandText = "delete lbl where codsoc = @codsoc and codtbl = 'cri'";
  275.  
  276.            SqlParameter pCodsoc = cmd.CreateParameter();
  277.            pCodsoc.Direction = ParameterDirection.Input;
  278.            pCodsoc.ParameterName = "codsoc";
  279.            pCodsoc.Size = 18;
  280.            pCodsoc.SqlDbType = SqlDbType.Decimal;
  281.            cmd.Parameters.Add(pCodsoc);
  282.  
  283.            pCodsoc.Value = codsoc;
  284.            cmd.ExecuteNonQuery();
  285.  
  286.            cmd.CommandText = "insert into cri (codsoc, codtbl, sigti1, sigti2, codlan, label) values (@codsoc, 'cri', @codcri, '', @codlan, @label)";
  287.            SqlParameter pCodfam = cmd.CreateParameter();
  288.            pCodfam.Direction = ParameterDirection.Input;
  289.            pCodfam.ParameterName = "codcri";
  290.            pCodfam.Size = 6;
  291.            pCodfam.SqlDbType = SqlDbType.VarChar;
  292.            cmd.Parameters.Add(pCodfam);
  293.            SqlParameter pCodlan = cmd.CreateParameter();
  294.            pCodlan.Direction = ParameterDirection.Input;
  295.            pCodlan.ParameterName = "codlan";
  296.            pCodlan.Size = 3;
  297.            pCodlan.SqlDbType = SqlDbType.VarChar;
  298.            cmd.Parameters.Add(pCodlan);
  299.            SqlParameter pLabel = cmd.CreateParameter();
  300.            pLabel.Direction = ParameterDirection.Input;
  301.            pLabel.ParameterName = "label";
  302.            pLabel.Size = 50;
  303.            pLabel.SqlDbType = SqlDbType.VarChar;
  304.            cmd.Parameters.Add(pLabel);
  305.  
  306.            foreach (DataRow dr in dt.Rows)
  307.            {
  308.                pCodfam.Value = (string)dr["CODCRI"];
  309.                pCodlan.Value = (string)dr["CODLAN"];
  310.                pLabel.Value = (string)dr["LIBCRI"];
  311.                cmd.ExecuteNonQuery();
  312.            }
  313.        }
  314.        #endregion
  315.    }
  316. }


 
Ca me permet ici de lire des données depuis une base Oracle, et les intégrer dans une base SQL Server sans me soucier des problèmes de conversion de type notamment.
 
DataSet.WriteXml() génère un fichier XML contenant une XSD embarquée. Ce qui fait qu'on peut le relire automatiquement depuis un DataSet.ReadXml() sans se soucier de sa structure. Ici, je stocke plusieurs DataTable dans le même fichier, ce qui me permet de n'avoir qu'un seul gros fichier à me trimballer plutôt qu'une chiée qu'il me serait très chiant de gérer.


Message édité par MagicBuzz le 28-08-2007 à 14:34:16
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed