begin process at 2010 02 10 01:24:19
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Base de données

 > SPLIT SQL SANS TABLE TEMPORAIRE

SPLIT SQL SANS TABLE TEMPORAIRE


 Information sur la source

Note :
8 / 10 - par 1 personne
8,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Base de données Source .NET ( DotNet ) Classé sous :split, séparateur, sql Niveau :Débutant Date de création :25/02/2009 Date de mise à jour :10/03/2009 16:07:38 Vu / téléchargé :3 412 / 106

Auteur : TheOnlyMaX

Ecrire un message privé
Site perso
Commentaire sur cette source (21)
Ajouter un commentaire et/ou une note

 Description

Cliquez pour voir la capture en taille normale
Voici une fonction qui permet de splitter les données contenues dans une seule colonne avec séparateur, sans passer par une table temporaire.

Source

  • // Code source complet dans le zip !
  • public string SqlSplitSeparateur(ArrayList champsList, string separateur, int idxFirstCol)
  • {
  • string currentIndex = idxFirstCol.ToString(); // index de la 1ère colonne dans le fichier
  • string subStrPrec = "";
  • string colLen;
  • string sqlSelect = "";
  • int i = 0;
  • foreach (Champ champ in champsList)
  • {
  • i++;
  • if (i == 1)
  • {
  • // Pour la 1ère ligne, aller jusqu'à la 1ere occurence du séparateur
  • subStrPrec = "SUBSTRING(" +
  • TMP_COL + "," +
  • idxFirstCol + "," +
  • "CHARINDEX('" + separateur + "'," + TMP_COL + "," + idxFirstCol + ")-" + idxFirstCol + ")";
  • }
  • else
  • {
  • // Lignes suivantes : calcul de la longueur de la colonne (de currentIndex au prochain séparateur / fin)
  • if (i < champsList.Count)
  • colLen = "(CHARINDEX('" + separateur + "'," + TMP_COL + "," + currentIndex + ")" + "-(" + currentIndex + "))";
  • else
  • colLen = "999"; // Dernière colonne : aller jusqu'en fin de ligne (il n'y a plus de séparateur !) - NB : on pourrait le calculer : colLen=LEN(TMP_COL)-currentIndex
  • subStrPrec = "SUBSTRING(" + TMP_COL + "," + currentIndex + "," + colLen + ")";
  • }
  • sqlSelect += "RTRIM(LTRIM(" + subStrPrec + ")),";
  • // MAJ de la position dans la ligne
  • currentIndex += "+LEN(" + subStrPrec + ")+" + separateur.Length;
  • }
  • sqlSelect = sqlSelect.Remove(sqlSelect.LastIndexOf(','), 1); // On retire la virgule de la fin
  • return "SELECT " + sqlSelect + " FROM " + TABLE_TMP;
  • }
// Code source complet dans le zip !

public string SqlSplitSeparateur(ArrayList champsList, string separateur, int idxFirstCol)
{
    string currentIndex = idxFirstCol.ToString(); // index de la 1ère colonne dans le fichier
    string subStrPrec = "";
    string colLen;
    string sqlSelect = "";

    int i = 0;
    foreach (Champ champ in champsList)
    {
        i++;
        if (i == 1)
        {
            // Pour la 1ère ligne, aller jusqu'à la 1ere occurence du séparateur
            subStrPrec = "SUBSTRING(" +
                TMP_COL + "," +
                idxFirstCol + "," +
                "CHARINDEX('" + separateur + "'," + TMP_COL + "," + idxFirstCol + ")-" + idxFirstCol + ")";
        }
        else
        {
            // Lignes suivantes : calcul de la longueur de la colonne (de currentIndex au prochain séparateur / fin)
            if (i < champsList.Count)
                colLen = "(CHARINDEX('" + separateur + "'," + TMP_COL + "," + currentIndex + ")" + "-(" + currentIndex + "))";
            else
                colLen = "999"; // Dernière colonne : aller jusqu'en fin de ligne (il n'y a plus de séparateur !) - NB : on pourrait le calculer : colLen=LEN(TMP_COL)-currentIndex

            subStrPrec = "SUBSTRING(" + TMP_COL + "," + currentIndex + "," + colLen + ")";
        }
        sqlSelect += "RTRIM(LTRIM(" + subStrPrec + ")),";

        // MAJ de la position dans la ligne
        currentIndex += "+LEN(" + subStrPrec + ")+" + separateur.Length;
    }

    sqlSelect = sqlSelect.Remove(sqlSelect.LastIndexOf(','), 1); // On retire la virgule de la fin
    return "SELECT " + sqlSelect + " FROM " + TABLE_TMP;
}

 Conclusion

Dans un soucis d'optimisation du temps de traitement d'un volume important de données, j'ai cherché pendant pas mal de temps une fonction de split avec séparateur sans table temporaire, mais en vain.

J'ai eu besoin d'une telle fonction, car je faisais un bulk insert dans une table temporaire dans laquelle je devais supprimer des lignes de header / footer, avant de splitter les lignes qui m'intéressaient pour les intégrer dans la table finale.

J'attends vos commentaires, merci.

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Historique

25 février 2009 12:39:30 :
oops
25 février 2009 14:04:50 :
modif
26 février 2009 15:14:05 :
explications du code + exemple
10 mars 2009 16:04:01 :
Vraie table temporaire !
10 mars 2009 16:07:38 :
Vraie table temporaire

 Sources du même auteur

Source avec Zip Source avec une capture SURVEILLER FICHIERS CRÉÉS AVEC FILESYSTEMWATCHER AMÉLIORÉ

 Sources de la même categorie

Source avec Zip Source avec une capture Source .NET (Dotnet) DEMO_XML_BASE_DE_DONNÉES par DanMor498
Source avec Zip Source avec une capture Source .NET (Dotnet) EXPORTER SCRIPT SQL SQL SERVER (CREATE - DROP - INSERT - TAB... par citt
Source avec Zip Source .NET (Dotnet) BULKINSERT EN C# par Malkuth
Source avec Zip Source .NET (Dotnet) MOTEUR CSV ET RECORDSET PERSO par SwitchApocalyps
Source avec Zip Source .NET (Dotnet) INSERER UN PHOTO DANS UNE BASE DE DONNÉES MYSQL/ORACLE par herold

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture Source .NET (Dotnet) GENEREREQUÊTE par donald42
Source avec Zip Source avec une capture Source .NET (Dotnet) REPOSITORY GENERATOR (FOR ENTERPRISE LIBRARY) par romagny13
Source .NET (Dotnet) SIMPLIFIER LES REQUETE SQL par student_001
Source avec Zip Source avec une capture Source .NET (Dotnet) SQL2005SCRIPTEXTRACTOR (POUR SUBVERSION(SVN)) par zebobo5
Source avec Zip Source .NET (Dotnet) BULK COPY EXCEL VERS SQL SERVER par MorpionMx

Commentaires et avis

Commentaire de tmcuh le 25/02/2009 12:54:54

Pourrais-tu donner un exemple de données, car je vois pas à quoi ça sert...

Commentaire de Bidou le 25/02/2009 13:56:48 administrateur CS

Pas tout à fait compris non plus.
La classe StringBuilder a t-elle été retirée du framework?

Commentaire de TheOnlyMaX le 25/02/2009 14:13:53

Dans le principe, l'instruction SELECT générée me permet de faire un "INSERT INTO tableFinale [Select généré]"

J'ai adapté rapidement ma source pour la mettre à dispo ici, mais ça mérite encore quelques modifs et un exemple. Je m'en occupe dans la semaine, merci.

Commentaire de TheOnlyMaX le 26/02/2009 15:16:17

Voilà, j'ai joint un petit script SQL de test dans le zip, et j'ai nettoyé le code.

Commentaire de tmcuh le 26/02/2009 16:03:11 8/10

L'idée est intéressante, mais cela aurait été beaucoup plus puissant si tu recrée ta table final dynamiquement. En effet, dès le départ compter le nombre de ; pour connaitre le nombre de colonnes.
Mais pourquoi faire les étapes : 1) importer fichier csv dans sql    2) découper l'information dans sql.
Pourquoi ne pas avoir fait l'opération depuis ton code c#, qui est quand même assez puissant et rapide pour permettre ce type de man½uvre (découpe de l'information) avant de l'importer dans SQL.

Cordialement

Commentaire de TheOnlyMaX le 26/02/2009 17:15:38

Tmuch,
1) Effectivement Tmcuh, on peut améliorer la source et rendre le nombre de colonnes dynamiques, mais je n'en ai ni l'utilité, ni le temps !

2) J'aurais pu traiter chaque ligne une par une, en faisant un select sur chaque ligne, en splittant la chaine sous C#, puis en générant une instruction INSERT. Mais cela pose de sérieux problèmes de perfs : pour 10 millions de lignes, ca fait 20 millions d'instructions envoyées au serveur !
Les fonctions sql utilisées ici (CHARINDEX, SUBSTRING, LEN, RTRIM, LTRIM) sont extrêmement peu coûteuses, même par rapport à une fonction sql qui utilise une table temporaire, et qui génère autant d'INSERT qu'il y a de colonne, et ce pour chaque ligne + l'INSERT dans la table finale, donc pour 10M de lignes, et 10 colonnes, ca fait 110 millions d'INSERT !

Commentaire de Malkuth le 26/02/2009 18:37:27

Je rejoins tmcuh, je pense que la método n'est pas optimal(mais on as pas forcerment toute les infos...)

a lors actuel tu fais :
BulkInsert d'un CSV en table intermédiaire
Nettoyage/insertion final

Clairement SQL n'est pas le meilleur choix pour le nétoyage(qq bench pourrons facilement le prouver).

je pense que tu aurais plutot du utilisé la métho suivante :
Lecture/Nétoyage du CSV (netoyage a la volée)
bulkinsert dans la table final.

tu dispose de la classe [System.Data.SqlClient.SqlBulkCopy] qui permet de commander facilement l'insertion Bulk directement depuis C#.

D'abord ca me semble plus logique et surtout, Ca permet de déporter la charge de nettoyage sur une machine différente (voir plusieurs...)

Commentaire de TheOnlyMaX le 26/02/2009 19:45:56

Lecture/Nétoyage du CSV (netoyage a la volée) ==> Plus d'infos là-dessus ?
Je n'ai pas trouvé comment modifier un fichier texte sans le ré-écrire entièrement... Est-ce possible ?

Commentaire de Malkuth le 26/02/2009 22:18:48

Lecture/ nétoyage à la volé ca veut dire en gros tu lis une ligne du fichier, tu la notoie aussitôt et tu la transmet au serveur.

donc, lire une à une les ligne d'un fichier texte ca donne un truc du genre :

        public IEnumerable<string> lireLignes(string cheminfichier)
        {
            string ligne;
            using (TextReader txtstrm = File.OpenText(cheminfichier))
                while ((ligne = txtstrm.ReadLine()) != null)
                    yield return ligne;
        }

ensuite on doit les néttoyé on va séparé ca en 2 phases : l'extraction des données puis leur vérification pour des d'optimisation certain les 2 étape peuvent être plus ou moins mélé, au passage on crée une petite structure légére pour conservé les donnée nétoyé (et typé d'ailleurs...)


        public struct donnéestructurées
        {
            public string col_nom;
            public int col_age;
        }


        public IEnumerable<donnéestructurées> nettoyerlignes(IEnumerable<string> lignesbrute)
        {
            foreach (string ligne in lignesbrute)
            {
                donnéestructurées lignepropre = new donnéestructurées();

                //extraction des données
                string[] donnesintermediaire = ligne.Split(';');
                if (donnesintermediaire.Length != 2) continue;// si ca va pas on reprend le traitement a la ligne suivante
                lignepropre.col_nom = donnesintermediaire[0];
                int.TryParse(donnesintermediaire[1],out lignepropre.col_age);

                //Vérification de la bonne conformitée des données
                if (lignepropre.col_age < 10
                || lignepropre.col_age > 20
                || string.IsNullOrEmpty(lignepropre.col_nom))
                    // si ca va pas on reprend le traitement a la ligne suivante
                    continue;

                //Si tout va bien on envoie la ligne propre et on continue ;)
                yield return lignepropre;
            }
        }

maintenant, on a donc moyen des récupéré des donnée propres juste en faisant:

nettoyerlignes(lireLignes("monfic.csv"));

(je ne sais pas ta connaissance éxacte de C# mais si tu fais une exécution pas à pas de ce code tu véras que contrairement au aparance, il lit une ligne, la nétoi, lit la suivante, la nétoi etc, si ce mécanisme t'est inconnue renseigne toi sur le mot clef yield, si connais déjà tout ca, oublie mon commentaire ;) )


alors maintenant on as des donnée propre, il faut les envoyés au serveur, solution un, une suite d'insert, a oublier dans ton cas on est bien d'accord, Solution 2, envoyé les donnée sous forme XML trés bien pour un volume moyen mais pas assez rapide pour 10millions de lignes ^^ solution 3 le Bulk insert (ouai on est d'accord ^^) et pour le bulkinsert y'a une classe pas mal foutu en .NET : System.Data.SqlClient.SqlBulkCopy

ca permet d'accéder a la plupart des fonctionnalité  de Bulkinsert directement depuis le code(donc sans avoir besiion de réécrire les données dans un fichier.

voici un exemple


        public void Insererligne(IEnumerable<donnéestructurées> données)
        {
            using(SqlConnection con = new SqlConnection("maconnexion"))
            {
                using (SqlBulkCopy cpy = new SqlBulkCopy(con))
                {
                    cpy.BulkCopyTimeout = 100000;
                    cpy.BatchSize = 100000;
                    cpy.DestinationTableName = "matablefinal";
                    cpy.ColumnMappings.Add("col_nom", "Nom");
                    cpy.ColumnMappings.Add("col_age", "Age");

                    DataTable donnéespretes = new DataTable();

                    DataColumn cNom = new DataColumn("col_nom", typeof(string));
                    donnéespretes.Columns.Add(cNom);

                    DataColumn cAge = new DataColumn("col_age", typeof(int));
                    donnéespretes.Columns.Add(cAge);
                    int nblignes = 0;
                    foreach (donnéestructurées donné in données)
                    {
                        DataRow row = donnéespretes.NewRow();
                        row[cNom] = donné.col_nom;
                        row[cAge] = donné.col_nom;
                        donnéespretes.Rows.Add(row);

                        if((nblignes%100000)==0)
                            cpy.WriteToServer(donnéespretes);
                    }
                }
            }
        }

donc maintenant tu fais : Insererligne(nettoyerlignes(lireLignes("monfic.csv")));
et ca roule.

Le code es pas optimiser pour 2 sous mais même en l'état il doit être bien plus performant.

Commentaire de Malkuth le 26/02/2009 22:22:29

oups, aprés la boucle foreach dans Insererligne il manque :

                        if((nblignes%100000)!=0)
                            cpy.WriteToServer(donnéespretes);

Commentaire de tmcuh le 27/02/2009 08:55:04

Très intéressant ton code malkuth. je connaissais la méthode bulkinsert, mais ton optimisation par le yield est intéressante.

Commentaire de Malkuth le 27/02/2009 09:04:15

en fait on remarque que le WriteToServeur peut aussi utilisé une interface IDataReader en paramêtre, donc pour allez un peu plus loin le mieux serais d'encapsuler les 2 fonctions dans une classe implémentant IDataReader ce qui évite de se retrouver a gérer un buffer en DataTable (j'ai horreur de ces objets DataRow/DataTable etc...) et la on aurais vraiment un programm entiérement "A la Volé" mais bon cette interface est un peu longue a implémenter pour un commentaire...peut être un code prochain.

Commentaire de TheOnlyMaX le 27/02/2009 12:02:18

Ta solution est extrêmement intéressante Malkuth ! Merci pour ces infos, je ne connaissais ni le yield ni le SqlBulkCopy.
Cependant, je crains que les perfs au niveau de la mémoire vive ne soient moins bonnes ? Sur un serveur d'application, c'est très important ! 100 000 lignes en mémoire, ca commence à faire... Et si on baisse cette valeur ?
-> Pourquoi ne pas faire un benckmark ?

Commentaire de Malkuth le 27/02/2009 12:51:00

Alors en fait, si tu baisse la valeur, c'est aussi baisser l'intêret du bulkcopy (aprés on peu tester différentes valeurs et en trouver une correct dans ton cas de maniére empirique).

Ensuite, ce code peux êtres déployé sur une autre serveur donc on déplace le probléme de charge mémoire/Travaille vers une machine qui n'auras pas la même importance dans le process glabal (voir dédier).

enfin, comme je le disait dans le dernier commentaire la fonction de transfert au serveur accept les interface IDatareader, celle ci représente un flux de lecture de donnée en avant uniquement, elle n'as donc pas besion de "Buffériser" les données, en gros pour expilquer l'interface:

une fonction ReadNext qui avannce d'une ligne le flux.
des fonction GetInt32(int col)/GetString(int col)/... qui lise la donnée de la [col] dans la ligne courante.
plusieur fonctions qui permette de gérés les métadonnée(nom de colone,type des colones etc.

En utilisant les fonction précédante et en les encapsulant dans une classe implémantant cette interface, on peu arrivé a ne jamais avoir plus d'une ligne de donné en mémoire.

Attention, en disant cela jetiens a présisé que je ne connait pas le fonctionnement interne de l'objet SqlBulkCopy, a t'il un buffer interne? a voir... un point important aussi, il faut bien pensé paramettré le Timeout car si l'opération est longue celui ci est vite dépassé et mettre une valeur trop longue peux complétement bugé le tout.

Enfin l'objet SqlBulkCopy posséde plusieur constructeur simpathique dont notement un constructeur permetant de spécifier une transaction et les option de Bulk (déclanchement des triggers,gestion des NULL/AutoIncrément etc, ce qui le rend particuliérement complet.

Un dernier point intérréssant de cette technique est sa souplesse il est trés facile d'introduire plusieurs couches de travaille intermédiare et de faire un code trés modulaire. ex :

Ajout d'un process de validation Métier:

public IEnumerable<donnéestructurées> ValidationMetier IEnumerable<donnéestructurées> donnéesavérifier)
        {
            foreach (donnéestructurées lignepropre in donnéesavérifier)
            {
                if (lignepropre.col_age < 10
                || lignepropre.col_age > 20
                || string.IsNullOrEmpty(lignepropre.col_nom))
                    // si ca va pas on reprend le traitement a la ligne suivante
                    continue;

                //Si tout va bien on envoie la ligne propre et on continue ;)
                yield return lignepropre;
            }
        }

Ce qui permet maintenant de faire :

Insererligne(ValidationMetier(nettoyerlignes(lireLignes("monfic.csv"))));

Maintenant, admettons que le fichier de départ est maintenant un XML :

public IEnumerable<donnéestructurées> LireDonnéesXML(string fichierxml)
{
....
yield return ...;
....
}

On peut mtn faire :
Insererligne(ValidationMetier(LireDonnéesXML("monfic.xml")))



La ca devient trés simpa car on a pu reprendre facilement le code commun sans ce prendre la tête et en gardant une compatibilité avec l'ancien code qui peut toujours servir si les deux type de fichier sont utilisé...

Enfin cette maniére de faire permet trés facilemet de test chaque fonction hors context et de valider son process:

public IEnumerable<string> fauxlecteur()
{
    yield return "Tom;10;";
    yield return "Rom;20";
    yield return "Tiemh";
}
public void VérificationNetoyage(IEnumerable<donnéestructurées> données)
{
    IEnumerator<donnéestructurées> enum = données.GetEnumerator();

    if(!enum.MoveNext()) throw new Exception();
    if(enum.Current.col_nom!= "Tom") throw new Exception();
    if(enum.Current.col_age!= 10) throw new Exception();

    if(!enum.MoveNext()) throw new Exception();
    if(enum.Current.col_nom!= "Rom") throw new Exception();
    if(enum.Current.col_age!= 20) throw new Exception();

    if(enum.MoveNext()) throw new Exception();
}

maintenant si on fais : VérificationNetoyage(nettoyerlignes(fauxlecteur())); et qu'on obtient une exception alors le nétoyage ne fonctionne pas comme on l'attendrais!


Vu que ca à l'air de plaire, je vais trouvé un peux de temps se weekend pour faire une source d'exemple complette.

Commentaire de rhwy le 27/02/2009 15:43:58

si tu cherchais au départ un sql split voici un code que j'ai posté il y a quelques temps:

IF EXISTS (SELECT * FROM DBO.SYSOBJECTS WHERE ID = OBJECT_ID(N'fSplit') AND XTYPE IN (N'FN', N'IF', N'TF'))
    DROP FUNCTION fSplit
GO

CREATE FUNCTION fSplit(
    @String NVARCHAR (4000), @Delimiter NVARCHAR (10)
)
RETURNS @T TABLE ([Value] NVARCHAR(4000))
BEGIN
    DECLARE @NEXTSTRING NVARCHAR(4000)
    DECLARE @POS INT,@DELIM_SIZE INT
    DECLARE @NEXTPOS INT

    SELECT
        @NEXTSTRING = '',
        @String = @String + @Delimiter,
        @DELIM_SIZE = LEN(@Delimiter)

    SET @POS = CHARINDEX(@Delimiter,@String)
    SET @NEXTPOS = 1

    WHILE (@POS <>  0)  
    BEGIN
        SET @NEXTSTRING = SUBSTRING(@String,1,@POS - 1)
        INSERT INTO @T ( [VALUE]) VALUES (@NEXTSTRING)
        SET @String = SUBSTRING(@String,@POS +@DELIM_SIZE,LEN(@String))
        SET @NEXTPOS = @POS
        SET @POS  = CHARINDEX(@Delimiter,@String)
    END

RETURN
END
GO

Ce qui pourra s'utiliser directement par:

-- version directe
SELECT * FROM dbo.fSplit('a1--b2--c3','--')
-- avec une jointure
SELECT * FROM MaTable A INNER JOIN dbo.fSplit('a,b,c',',') B ON A.id = B.Value


et si tu as beaucoup de lignes à traiter d'un bloc, je te conseillerai meme de faire un cross apply et de tout traiter directement en sql, voici un exemple:


IF EXISTS (SELECT * FROM sys.tables WHERE name = 'splitData')
DROP TABLE splitData
GO

CREATE TABLE splitData (id INT IDENTITY(1,1), data NVARCHAR(2000))
GO

INSERT INTO splitData (data) VALUES (CAST(NEWID() AS NVARCHAR(MAX)))
GO 5000

SELECT A.id,B.Value
FROM splitData A
CROSS APPLY dbo.fSplit(A.data,'-') B






Commentaire de tmcuh le 27/02/2009 15:56:31

rhwy, tes connaissances T-SQL sont impressionnantes. Je viens d'apprendre l'existance du cross apply ^^ et du go 5000 (très pratique).

Commentaire de Malkuth le 27/02/2009 16:59:28

Le cross aplly est effectivement trés pratique mais je doute vraiment que les performances soient au rendez vous.

sur 10 000 000 de lignes traitée la charge de travail deviens vraiment colosale et je crois que chaque partie d'un produit doit être utilisé pour ce qu'il sait le mieux faire, et pour moi C# est bien plus performant en transformation linéaire de donnée que SQL Serveur.

Sle projet avais été pure SQL pourquoi pas (enfin je pense que TheOnlyMaX à une solution plus performante) mais étant donnée qu'il nous présente un code C# j'en conclu qu'il a donc du C# dans son projet donc autant l'utiliser là ou il est mieux indiquer...

@TheOnlyMaX : serait'il possible que tu me passe un de tes fichiers d'importation type? (un Fake bien entendu) avec la table dans laquelle les données sont placé au final(déclaration SQL) afin de consolidé mon code pour un cas réaliste? pour infos je suis à un temps de 11 sec de traitement pour lecture/néttoyage/vérification métier sur un fichier de 10 000 000 de ligne et 3 colones (reste a traiter le bulkinsert en lui même) et ce code est trés loin d'un code optimisé.

Commentaire de Malkuth le 27/02/2009 20:00:17

Voilà un exemple complet avec implémantation du IDatareader :
http://www.csharpfr.com/codes/BULKINSERT-CSHARP_49377.aspx

Commentaire de TheOnlyMaX le 10/03/2009 16:45:59

En ajoutant un # devant le nom de la table temporaire, elle devient une "vraie" table temporaire pour SQL Serveur, donc elle reste uniquement en mémoire, et on améliore considérablement les perfs (jai mis à jour le zip). Donc au final, on se retrouve avec le même mode de fonctionnement qu'avec ton code Malkuth (mise en mémoire des lignes, nettoyage, écriture des lignes propres).

@Malkuth : J'ai testé la classe SqlBulkCopy en utilisant une connexion OleDb au fichier. L'implémentation est bien plus simple. En gros, ça donne :

SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(mySqlConnection, SqlBulkCopyOptions.TableLock, mySqlTransaction);
sqlBulkCopy.DestinationTableName = "TABLE_FINALE";
sqlBulkCopy.BulkCopyTimeout = 10*60;

string folder = @"D:\test";
string connectionStringTextFile = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + folder + ";Extended Properties='Text;'";
System.Data.OleDb.OleDbConnection connectionTextFile = new System.Data.OleDb.OleDbConnection(connectionStringTextFile);
connectionTextFile.Open();
string fileName = "fichier.txt"; // Attention, toutes les extensions ne sont pas prises en compte !
int nbLignesHeader = 3;
System.Data.OleDb.OleDbDataAdapter da = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM " + fileName, connectionTextFile);
DataSet dsFile = new DataSet(fileName);
da.Fill(dsFile, fileName);
DataTable dtFile = dsFile.Tables[fileName];

int idxLigne=0;
foreach (DataRow row in dtFile.Rows)
{
// Ignorer les lignes de Header
if (idxLigne++ < nbLignesHeader) continue;

ligne = row[0].ToString(); // ligne entière

// Splitter la ligne
tabLigne = ligne.Split(';');

for (int i=0; i<tabLigne.Length; i++)
{
valeur = tabLigne[i];
valeur = valeur.ToString().Trim();
rowOut[i] = valeur;
}
dtOut.Rows.Add(rowOut);

if (idxLigne % 10000 == 0)
{
sqlBulkCopy.WriteToServer(dtOut);
dtOut.Rows.Clear();
}
}
if (dtOut.Rows.Count > 0)
{
sqlBulkCopy.WriteToServer(dtOut);
dtOut.Rows.Clear();
}
connectionTextFile.Close();



-> Maintenant, si on imagine une procédure stockée classique qui splitte les données en utilisant une vraie table temporaire, est-on encore gagnant avec ma méthode ?

Commentaire de TheOnlyMaX le 10/03/2009 18:14:34

Par contre, je n'ai pas poussé les tests très loin à propos du code ci-dessus, mais les perfs en mémoire étaient assez horribles (elle gonfle), et les perfs en terme de temps sont bien moins bonnes...

Commentaire de TheOnlyMaX le 11/03/2009 15:14:14

Oops, le code ci-dessus n'utilise pas la classe SqlBulkCopy bien sûr, mais juste une connexion OleDb au fichier !

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

split chaine sans séparateur [ par babe59 ] Bonjourj'ai une chaine des 40 caractères (longueur fixe). Cette chaine contient que des chiffres et est renseignée par paquet de 10 chiffres (soit 4 p sql [ par angedb9 ] salut,j'ai deux tables (personnel: num_pers,nom_pers,pre_pers et type_personnel: num_typ_pers,lib_typ_pers) quand je fait la sqlcommand "insert into p visual C++ .NET et sql server [ par isil4 ] Bonjour tout le monde,bon je suis débutante en visual studio, jai installé visual studio 2008 qui incluait sql server 2005 je crois car je l'ai trouvé linq to sql [ par laymouna98 ] salut à tous je veut ajouter à mon projet un fichier de type LinkObjets, c'est un fichier qui à l'extension .dbml, mais le problème c'est ce modèle n' Linq to sql [ par laymouna98 ] salutj'ai fait un pteit exemple avec linq et sql servermaintenent je veut bien changer la base de données au lieu de sql server en oracleest ce que je connexion à la base des données sql server en c# [ par baby85 ] bonjour,je suis débutante en c# et je veux savoir comment je peux se connecter à sql server en c#.merci pour votre réponse probleme en connexion à la base de données sql [ par conpf ] bonsoir,je veux faire une connexion à la base des données sql mais j ai une une probleme de connexion je sais pas pourquoivoila le code :string<font s connxion à la base des données sql server en c# [ par conpf ] bonsoir,je suis débutante en c# et je veux faire une connexion a la base des données sql server mais j'ai pa compris la réalisation de ces etapes :cré Adaptation de script [ par 4rocky4 ] Bonjour tout le monde,Je dois migrer une base Sql Server 2005 vers oracle 11g.J'ai obtenu un script de création des tables pour Oracle (fichier.sql) à Split avec choix paramétrés [ par whismeril ] Bonjour, je cherche à faire une selection entre une centaine de choix.J'ai bien des imbrication de if, mais je finis par ne plus savoir ou j'en suis.J


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Février 2010
LMMJVSD
1234567
891011121314
15161718192021
22232425262728

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,014 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales