IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

Votre Avis : Optimisation des performances pour le chargement de fichier CSV


Sujet :

Langage Delphi

  1. #1
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut Votre Avis : Optimisation des performances pour le chargement de fichier CSV
    Bonjour,

    Actuellement je fais un petit projet et je lis des fichiers CSv que je mets dans une StringGrid. Cependant, je me demande si le code peut être optimisé ou si justement le temps mis est satisfaisant.

    Tout d'abord, voici le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    procedure TForm1.Button1Click(Sender: TObject);
    var
    Fichier:TextFile;
    ligne,chaine:string;
    i,row,col:integer;
    begin
    row := 1;
      if OpenDialog1.Execute then
      begin
        If Not QueryPerformanceFrequency(FFrequency) Then
       Raise Exception.Create('Pas de compteur hautes performances.');
       QueryPerformanceCounter(FStart) ;
        AssignFile(Fichier,OpenDialog1.FileName);
        Reset(Fichier);
        while not Eof(Fichier) do
        begin
          col:=0;
          Readln(Fichier,ligne);
          for i := 1 to length(ligne) do
          begin
            if ((ligne[i]<>';') and (i<length(ligne))) then
              chaine:=chaine+ligne[i]
            else
            begin
              StringGrid1.ColCount:=col+1;
              StringGrid1.Cells[col,row-1]:=Trim(chaine);
              col:=col+1;
              chaine:='';
            end;
          end;
          row := row + 1;
          StringGrid1.RowCount:=row;
        end;
        CloseFile(Fichier);
        QueryPerformanceCounter(FStop) ;
        FElapsed := (FStop-FStart)/FFrequency ;
        Label2.Caption:='Temps d''execution : '+FormatFloat('0.000',FElapsed)+' secondes';
      end;
    end;
    Ensuite, j'ai effectué un test avec un fichier CSV de 6 colonnes et 43471 lignes. (2.03 Mo)
    Sur un P4 3.2Ghz HT avec 1 Go de mémoire dualChannel, cela donne le résultat suivant : 61 secondes.

    Aussi, pour faire une barre de progression lors du chargement du fichier, pouvez vous me donner quelques pistes ?

    Merci de votre attention

  2. #2
    Modérateur
    Avatar de Rayek
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2005
    Messages
    5 235
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 235
    Points : 8 504
    Points
    8 504
    Par défaut
    Avec le code ci-dessous, ca me remplie la StringGrid en environ 24-25s pour un fichier de 2,5mo avec 6 colonnes.
    Test fait sur un sempron 2200+ (1.5ghz)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    var
      lst,lstLigne: TStringList;
      i,j : integer;
      Begintime, endtime : integer;
    begin
     
      if OpenDialog1.Execute then
      begin
        BeginTime := GetTickCount;
        lst := TStringList.Create;
        lstLigne := TStringList.Create;
        try
          lst.LoadFromFile(OpenDialog1.FileName);
          StringGrid1.RowCount := lst.Count + 1;
          StringGrid1.ColCount := 1;
          for i := 0 to lst.Count -1 do
          begin
            lstLigne.Text := lst.Strings[i];
            lstLigne.Text := StringReplace(lstLigne.text,';',#13#10,[rfReplaceAll]);
            if lstLigne.Count > StringGrid1.ColCount then
              StringGrid1.ColCount := lstLigne.count;
            for j := 0 to lstLigne.Count -1 do
              StringGrid1.Cells[j,i+1] := lstLigne.Strings[j];
            ProgressBar1.Position := i * 100 div (lst.count -1);
            Application.ProcessMessages;
          end;
          EndTime := GetTickCount;
          Showmessage(IntToStr(EndTime - BeginTime));
        finally
          lst.free;
          lstligne.free;
        end;
      end;
    end;
    [edit]
    oups erreur , ca me met environ 2374ms de traitement pour le même fichier et la même taille (me suis loupé dans la conversion )

    6 colonnes ; env 35 000 enregistrements = 2374ms
    [/edit]

  3. #3
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    chaine:=chaine+ligne[i]
    ceci est lent, il vaut mieux allouer chaine dans sa taille finale (si l'on peut la précalculée) puis remplir avec des CopyMemory ou Move PChar ...

    Ensuite, je vais te passer un code qui devrait te donner des idées, c'est un format de fichier genre Excel primitif, mais c'est surtout pour le remplissage de la Grille que cela devrait t'intéresser
    voir TStringGridCrossManager.LoadFromFile

    ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    //------------------------------------------------------------------------------
    // Auteur : Stéphane Prévot dit Shai Le Troll, le Collègue de Shon             -
    //------------------------------------------------------------------------------
     
    unit uCrossFile;
     
    interface
     
    uses
       Windows, Messages, SysUtils, Classes, Math, Grids;
     
    { TFileCross }
     
    type
       TFileCross = class;
     
       PCrossFormatHeaderItemFile = ^TCrossFormatHeaderItemFile;
       TCrossFormatHeaderItemFile = packed record
          Position: TPoint;
          Length: Integer;
       end;
     
       PCrossFormatItemFile = ^TCrossFormatItemFile;
       TCrossFormatItemFile = packed record
          Header: TCrossFormatHeaderItemFile;
          Data: array of Byte;
       end;
     
       TFileCrossProgressEvent = procedure(Filer: TFileCross; var Position, Step, Size: Integer) of object;
     
       TCrossStringList = class(TList)
       private
          FMaxCount: TPoint;
          function Get(Index: Integer): TStrings;
          procedure Put(Index: Integer; Item: TStrings);
       public
          constructor Create();
          destructor Destroy(); override;
          procedure Clear(); override;
          function Add(): Integer;
          procedure Delete(Index: Integer);
     
          property Items[Index: Integer]: TStrings read Get write Put; default;
          property MaxCount: TPoint read FMaxCount;
       end;
     
       TFileCross = class(TObject)
       private
          FList: TCrossStringList;
          FStringGrid: TStringGrid;
          FPosition: Integer;
          FSize: Integer;
          FOnProgress: TFileCrossProgressEvent;
          procedure DoProgress(Step: Integer);
       public
          constructor Create();
          destructor Destroy(); override;
          procedure Clear();
          function LoadFromFile(const FileName: string): Cardinal;
          function SaveToFile(const FileName: string): Cardinal;
     
          property StringGrid: TStringGrid read FStringGrid write FStringGrid;
          property OnProgress: TFileCrossProgressEvent read FOnProgress write FOnProgress;
          property Items: TCrossStringList read FList;
       end;
     
       TStringGridCrossManager = class(TObject)
       private
          FFileCross: TFileCross;
          FStringGrid: TStringGrid;
          FOnClear: TNotifyEvent;
          FOnProgress: TFileCrossProgressEvent;
          FFileDuration: Cardinal;
          function DoClear(): Cardinal;
       public
          constructor Create(StringGrid: TStringGrid);
          destructor Destroy(); override;
          function LoadFromFile(const FileName: string): Cardinal;
          function SaveToFile(const FileName: string): Cardinal;
     
          property StringGrid: TStringGrid read FStringGrid write FStringGrid;
          property OnClear: TNotifyEvent read FOnClear write FOnClear;
          property OnProgress: TFileCrossProgressEvent read FOnProgress write FOnProgress;
          property FileDuration: Cardinal read FFileDuration;
       end;
     
    implementation
     
     
    { TCrossStringList}
     
    //------------------------------------------------------------------------------
    constructor TCrossStringList.Create();
    begin
       inherited Create();
    end;
     
    //------------------------------------------------------------------------------
    destructor TCrossStringList.Destroy();
    begin
       Clear();
     
       inherited Destroy();
    end;
     
    //------------------------------------------------------------------------------
    procedure TCrossStringList.Clear();
    var
       il: integer;
    begin
       for il := 0 to Count - 1 do
       begin
          if Assigned(inherited Items[il]) then
          begin
             TStringList(inherited Items[il]).Free();
          end;
       end;
       inherited Clear();
    end;
     
    //------------------------------------------------------------------------------
    function TCrossStringList.Get(Index: Integer): TStrings;
    begin
       Result := TStrings(inherited Items[Index]);
    end;
     
    //------------------------------------------------------------------------------
    procedure TCrossStringList.Put(Index: Integer; Item: TStrings);
    begin
       inherited Items[Index] := Item;
    end;
     
    //------------------------------------------------------------------------------
    function TCrossStringList.Add(): Integer;
    begin
       Result := inherited Add(TStringList.Create());
    end;
     
    //------------------------------------------------------------------------------
    procedure TCrossStringList.Delete(Index: Integer);
    begin
       if (Index < 0) or (Index >= Count) then
       begin
          if Assigned(inherited Items[Index]) then
          begin
             TStrings(inherited Items[Index]).Free();
          end;
          inherited Delete(Index);
       end;
    end;
     
     
     
    { TFileCross }
     
    //------------------------------------------------------------------------------
    constructor TFileCross.Create();
    begin
       inherited Create();
     
       FList := TCrossStringList.Create();
    end;
     
    //------------------------------------------------------------------------------
    destructor TFileCross.Destroy();
    begin
       FList.Free();
     
       inherited Destroy();
    end;
     
    //------------------------------------------------------------------------------
    procedure TFileCross.Clear();
    begin
       if Assigned(FList) then
       begin
          FList.Clear();
       end;
    end;
     
    //------------------------------------------------------------------------------
    procedure TFileCross.DoProgress(Step: Integer);
    begin
       Inc(FPosition, Step);
       if Assigned(FOnProgress) then
       begin
          FOnProgress(Self, FPosition, Step, FSize);
       end;
    end;
     
    //------------------------------------------------------------------------------
    function TFileCross.LoadFromFile(const FileName: string): Cardinal;
    var
       iX, iY, iMaxX, iMaxY, OldX, OldY, IndexX, IndexY: Integer;
       CrossFile: file;
       ItemHeader: TCrossFormatHeaderItemFile;
       StrItem: string;
       AmtTransferred: Integer;
       StartTick, EndTick, TickPerSec: Int64;
    begin
       Result := 0;
       QueryPerformanceCounter(StartTick);
       try
          Clear();
     
          iMaxY := 0;
          iMaxX := 0;
          IndexY := 0;
          IndexX := 0;
     
          AssignFile(CrossFile, FileName);
          Reset(CrossFile, 1);
          try
             FPosition := 0;
             FSize := FileSize(CrossFile);
             DoProgress(0);
     
             while not EOF(CrossFile) do
             begin
                BlockRead(CrossFile, ItemHeader, SizeOf(ItemHeader), AmtTransferred);
                if AmtTransferred < SizeOf(ItemHeader) then
                begin
                   Exit;
                end;
                DoProgress(AmtTransferred);
     
                SetLength(StrItem, ItemHeader.Length);
                BlockRead(CrossFile, StrItem[1], ItemHeader.Length, AmtTransferred);
                if AmtTransferred < ItemHeader.Length then
                begin
                   Exit;
                end;
                DoProgress(AmtTransferred);
     
                if FList.Count <= ItemHeader.Position.Y then
                begin
                   OldY := FList.Count;
                   if FList.Capacity < ItemHeader.Position.Y then
                     FList.Capacity := ItemHeader.Position.Y;
                   for iY := OldY to ItemHeader.Position.Y do
                   begin
                      IndexY := FList.Add();
                   end;
                end
                else
                begin
                   IndexY := ItemHeader.Position.Y;
                end;
                iMaxY := Max(iMaxY, IndexY);
     
                if FList[IndexY].Count <= ItemHeader.Position.X then
                begin
                   OldX := FList[IndexY].Count;
                   if FList[IndexY].Capacity < ItemHeader.Position.X then
                     FList[IndexY].Capacity := ItemHeader.Position.X;
                   for iX := OldX to ItemHeader.Position.X do
                   begin
                      IndexX := FList[IndexY].Add('');
                   end;
                end
                else
                begin
                   IndexX := ItemHeader.Position.X;
                end;
                iMaxX := Max(iMaxX, IndexX);
     
                FList[IndexY][IndexX] := StrItem;
             end;
          finally
             FList.FMaxCount.Y := iMaxY;
             FList.FMaxCount.X := iMaxX;
             CloseFile(CrossFile);
          end;
       finally
          QueryPerformanceCounter(EndTick);
          QueryPerformanceFrequency(TickPerSec);
          Result := Round((EndTick - StartTick) / TickPerSec * 1000);
       end;
    end;
     
    //------------------------------------------------------------------------------
    function TFileCross.SaveToFile(const FileName: string): Cardinal;
    var
       iX, iY, iMinX, iMinY, iMaxX, iMaxY: Integer;
       CrossFile: file;
       ItemHeader: TCrossFormatHeaderItemFile;
       StrItem: string;
       StartTick, EndTick, TickPerSec: Int64;
       YStrings: TStrings;
     
       function _RowStart(): Integer;
       begin
          if Assigned(FStringGrid) then
            Result := FStringGrid.FixedRows
          else
            Result := 0;
       end;
     
       function _ColStart(): Integer;
       begin
          if Assigned(FStringGrid) then
            Result := FStringGrid.FixedCols
          else
            Result := 0;
       end;
     
       function _RowCount(): Integer;
       begin
          if Assigned(FStringGrid) then
            Result := FStringGrid.RowCount
          else
            Result := FList.Count;
       end;
     
       function _Rows(Index: Integer): TStrings;
       begin
          if Assigned(FStringGrid) then
            Result := FStringGrid.Rows[Index]
          else
            Result := FList.Items[Index];
       end;
     
    begin
       QueryPerformanceCounter(StartTick);
     
       AssignFile(CrossFile, FileName);
       // Nouveau Fichier
       Rewrite(CrossFile, 1);
       try
          iMaxY := FList.Count - 1;
          iMaxX := 0;
          iMinX := _ColStart();
          iMinY := _RowStart();
     
          for iY := 1 to iMaxY do
          begin
             iMaxX := Max(iMaxX, FList[iY].Count - 1);
          end;
          FList.FMaxCount.Y := iMaxY;
          FList.FMaxCount.X := iMaxX;
     
          FPosition := 0;
          FSize := iMaxY * iMaxX;
          DoProgress(0);
     
          // Je commence par 1 pour Obtenir que les Données
          for iY := iMinY to _RowCount() - 1 do
          begin
             YStrings := _Rows(iY);
     
             for iX := iMinX to YStrings.Count - 1 do
             begin
                DoProgress(1);
     
                StrItem := YStrings.Strings[iX];
                if StrItem <> '' then
                begin
                   ItemHeader.Length := Length(StrItem);
                   ItemHeader.Position.X := iX - iMinX;
                   ItemHeader.Position.Y := iY - iMinY;
                   BlockWrite(CrossFile, ItemHeader, SizeOf(ItemHeader));
                   BlockWrite(CrossFile, StrItem[1], ItemHeader.Length);
                end;
             end;
          end;
       finally
          CloseFile(CrossFile);
     
          QueryPerformanceCounter(EndTick);
          QueryPerformanceFrequency(TickPerSec);
          Result := Round((EndTick - StartTick) / TickPerSec * 1000);
       end;
    end;
     
    { TStringGridCrossManager }
     
    //------------------------------------------------------------------------------
    constructor TStringGridCrossManager.Create(StringGrid: TStringGrid);
    begin
       inherited Create();
     
       FFileCross := TFileCross.Create();
       FStringGrid:= StringGrid;
    end;
     
    //------------------------------------------------------------------------------
    destructor TStringGridCrossManager.Destroy;
    begin
       FFileCross.Free();
     
       inherited;
    end;
     
    //------------------------------------------------------------------------------
    function TStringGridCrossManager.DoClear: Cardinal;
    var
       StartTick, EndTick, TickPerSec: Int64;
    begin
       QueryPerformanceCounter(StartTick);
       try
          if Assigned(FOnClear) then
          begin
             FOnClear(Self);
          end;
       finally
          QueryPerformanceCounter(EndTick);
          QueryPerformanceFrequency(TickPerSec);
          Result := Round((EndTick - StartTick) / TickPerSec * 1000);
       end;
    end;
     
    //------------------------------------------------------------------------------
    function TStringGridCrossManager.LoadFromFile(const FileName: string): Cardinal;
    var
       iRow, iX: Integer;
       StartTick, EndTick, TickPerSec: Int64;
    begin
       Result := DoClear();
     
       FFileCross.OnProgress := FOnProgress;
       FFileDuration := FFileCross.LoadFromFile(FileName);
       Result := Result + FFileDuration;
     
       QueryPerformanceCounter(StartTick);
       try
          FStringGrid.RowCount := FFileCross.Items.MaxCount.Y + FStringGrid.FixedRows;
          FStringGrid.ColCount := FFileCross.Items.MaxCount.X + FStringGrid.FixedCols;
     
          for iRow := 0 to FFileCross.Items.Count - 1 do
          begin
             with FStringGrid.Rows[iRow + FStringGrid.FixedRows] do
             begin
                BeginUpdate();
     
                for iX := 0 to FFileCross.Items[iRow].Count - 1 do
                begin
                   Strings[iX + FStringGrid.FixedCols] := FFileCross.Items[iRow][iX];
                end;
                EndUpdate();
             end;
          end;
       finally
          QueryPerformanceCounter(EndTick);
          QueryPerformanceFrequency(TickPerSec);
          Result := Result + Round((EndTick - StartTick) / TickPerSec * 1000);
       end;
    end;
     
    //------------------------------------------------------------------------------
    function TStringGridCrossManager.SaveToFile(const FileName: string): Cardinal;
    begin
       FFileCross.OnProgress := FOnProgress;
       FFileCross.StringGrid := StringGrid;
       try
         Result := FFileCross.SaveToFile(FileName);
       finally
         FFileCross.StringGrid := nil;
       end;
       FFileDuration := Result;
    end;
     
    end.

  4. #4
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    Tout d'abord, merci de vos réponses !

    A Malatar : Ta méthode est vraiment plus rapide! Avec mon fichier, j'obtiens un résultat de 3 secondes soit 20 fois plus rapide !!!! Enorme ...

    Citation Envoyé par Malatar
    [edit]
    oups erreur , ca me met environ 2374ms de traitement pour le même fichier et la même taille (me suis loupé dans la conversion )

    6 colonnes ; env 35 000 enregistrements = 2374ms
    [/edit]
    J'avais remarqué également car la différence était vraiment trop importante sinon

    A ShaillLeTroll : Je me doutais bien que tu allais passer par là Quand il y a optimisation, tu as toujours un truc sous la main lol.
    Pour ton code, je vais l'analyser pour comprendre car je suis pas doué encore avec tout ça ... mais je vais m'y attarder un petit moment! Merci

  5. #5
    Modérateur
    Avatar de Rayek
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2005
    Messages
    5 235
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 235
    Points : 8 504
    Points
    8 504
    Par défaut
    En fait le chargement en mémoire du fichier (dans lst) fait gagner enormement de temps, car après le reste se traite directement en mémoire donc plus rapide que de la lecture en faisant du DirectToDisc + traitement comme tu le fais.

    Mais je pense que ma méthode atteindra ses limites avec de très gros fichiers et que la tienne sera plus rapide (mais bon quand je dis gros fichier, ca sera des fichiers 50-100mo et +, mais la faudra tester et trouver des fichiers CSV de cette taille )

  6. #6
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    Bon, avec un P4 3G 512Mo, j'ai fait ceci, cela dure 1100~1200 ms, pour un fichier généré par le code suivant (3,58 Mo)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    procedure TFrmTestFichier.BtnCreateFichierCSVClick(Sender: TObject);
    var
      I: Integer;
      FichierCSV: TextFile;
      LineCSV: string;
    begin
      AssignFile(FichierCSV, EdPathFileCSV.Text);
      try
        Rewrite(FichierCSV);
        for I := 1 to 43471 do
        begin
          LineCSV := Format('Ligne %d; Col 1 - %0:d; Col 2 - %0:d; Col 3 - %0:d; Col 4 - %0:d; Col 5 - %0:d', [I]);
     
          Writeln(FichierCSV, LineCSV);
        end;
     
      finally
         CloseFile(FichierCSV);
      end;
    end;
    Voici le Code de Lecture ... via ReadLn, l'Affectation à l'avance de RowCount est évidemment ce qui fait gagné le plus de temps ... le ReadLn est plus lent car il faut deux lecture pour compter les lignes ce qui se fait en une fois avec une TStringList ... Bien sur, moins il y a d'affichage de Progession, plus c'est rapide ! dans notre cas il y a un affichage sur 256 lignes, c'est presque plus important comme temps

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    procedure TFrmTestFichier.BtnReadFichierCSVClick(Sender: TObject);
    var
      I, iRow: Integer;
      FichierCSV: TextFile;
      LineCSV: string;
      ArrayCSV: Types.TStringDynArray;
      StartTick, EndTick, TickPerSec: Int64;
    begin
      QueryPerformanceCounter(StartTick);
     
      iRow := 0;
      AssignFile(FichierCSV, EdPathFileCSV.Text);
     
      Reset(FichierCSV);
      try
         while not Eof(FichierCSV) do
         begin
            Inc(iRow);
            Readln(FichierCSV);
         end;
      finally
         CloseFile(FichierCSV);
      end;
      ProgressBarCSV.Max := iRow div 255;
      ProgressBarCSV.Position := 0;
      ProgressBarCSV.Step := 1;
     
      try
        Reset(FichierCSV);
        StringGridCSV.ColCount := 0;
        StringGridCSV.RowCount := iRow;
     
        iRow := 0;
        while not EOF(FichierCSV) do
        begin
     
          Readln(FichierCSV, LineCSV);
          Explode(LineCSV, ArrayCSV, ';', False, '"');
          if StringGridCSV.ColCount < Length(ArrayCSV) then
            StringGridCSV.ColCount :=  Length(ArrayCSV);
     
          with StringGridCSV.Rows[iRow] do
          begin
            BeginUpdate();
            for I := Low(ArrayCSV) to High(ArrayCSV) do
              Strings[I] := ArrayCSV[I];
            EndUpdate();
            if not ByteBool(iRow) then
               ProgressBarCSV.StepIt();
          end;
          Inc(iRow);
        end;
     
      finally
         CloseFile(FichierCSV);
      end;
     
      QueryPerformanceCounter(EndTick);
      QueryPerformanceFrequency(TickPerSec);
      lblReadCSVTime.Caption := IntToStr(Round((EndTick - StartTick) / TickPerSec * 1000)) + ' ms';
    end;
    bon explode doit se trouver sur le forum mais le voici :
    Cela gère la présence d'un ; au milieu de " " ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
     
    {* -----------------------------------------------------------------------------
    la fonction Explode retourne un tableau de chaînes. Ce sont les sous-chaînes, extraites de S, en utilisant le séparateur Separator. cela peut servir pour lire du CSV
    @param S Chaine à découper
    @param A Tableau de Chaine qui recevra la découpe
    @param Separators Caractères qui délimitent une chaine pour la découpe
    @param ExcludeEmpty Si True, les Chaines vides ne sont pas insérés dans le Tableau
    @param Quotes Caractères qui délimitent une chaine pour la découpe contenant des Separators, n'importe quel séparateur peut commencer et terminé une chaine, une quote doublée est considéré comme valeur un quote dans la chaine
    @param KeepSeparators Si True, A contient les chaines et les séparateurs mais pas les Quotes, sinon (par défaut) A ne contient que les Chaines.
    @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !)
    ------------------------------------------------------------------------------ }
    function Explode(const S: string; out A: Types.TStringDynArray; const Separators: string; ExcludeEmpty: Boolean = False; const Quotes: string = ''; KeepSeparators: Boolean = False): Integer;
    var
      iLesSep: Integer;
      iLesQuote: Integer;
     
      function IsSeparator(C: Char): Integer;
      begin
        for Result := 1 to iLesSep do
          if C = Separators[Result] then
            Exit;
     
        Result := -1;
      end;
     
      function IsQuote(C: Char): Integer;
      begin
        for Result := 1 to iLesQuote do
          if C = Quotes[Result] then
            Exit;
     
        Result := -1;
      end;
     
    var
      iStr: Integer;
      iQuote: Integer;
      iLenS: Integer;
      iLenSS: Integer;
      iLenA: Integer;
      iAdded: Integer;
      iBegin: Integer;
      Quoted: Boolean;
      DoubleQuoted: Boolean;
      AlreadyDQ: Boolean;
      QuoteConcat: string;
      iOffQuote: Integer;
      LastIsSep: Boolean;
    begin
      iLenS := Length(S);
      iLesSep := Length(Separators);
     
      if (iLenS = 0) or (iLesSep = 0) then
      begin
        SetLength(A, 1);
        Result := 0;
        A[Result] := '';
        Exit;
      end;
     
      iLesQuote := Length(Quotes);
      for iQuote := 1 to iLesQuote do
        if IsSeparator(Quotes[iQuote]) > 0 then
          raise EParserError.CreateFmt('le Délimiteur "%s" ne peut pas être un Séparateur !', [Quotes[iQuote]]);
     
      Result := 0;
      iQuote := 0;
      for iStr := 1 to Length(S) do
      begin
        if IsSeparator(S[iStr]) > 0 then
          Inc(Result)
        else
          if IsQuote(S[iStr]) > 0 then
            Inc(iQuote);
      end;
     
      if Odd(iQuote) then
        raise EParserError.CreateFmt('Nombre de Délimiteur Incorrect : "%d" !', [iQuote]);
     
      LastIsSep := IsSeparator(S[iLenS]) > 0;
     
      if KeepSeparators then
        iLenA := Result * 2 + 1
      else
        iLenA := Result + 1;
      SetLength(A, iLenA);
      iLenSS := 0;
      iAdded := 0;
      Quoted := False;
      iOffQuote := 0;
      QuoteConcat := '';
      AlreadyDQ := False;
      iBegin := 1;
      if IsSeparator(S[1]) > 0 then
      begin
        if KeepSeparators then
        begin
          iBegin := 2;
          A[iAdded] := S[1];
          Inc(iAdded);
        end;
      end;
     
      for iStr := iBegin to iLenS do
      begin
        if not Quoted and (IsSeparator(S[iStr]) > 0) then
        begin
          if ExcludeEmpty and (iLenSS = 0) then
          begin
            if KeepSeparators then
            begin
              A[iAdded] := S[iStr];
              Inc(iAdded);
            end;
            iBegin := iStr + 1;
          end else
          begin
            if AlreadyDQ then
              A[iAdded] := QuoteConcat
            else
              A[iAdded] := Copy(S, iBegin, iLenSS);
     
            AlreadyDQ := False;
            Inc(iAdded);
     
            if KeepSeparators and (iBegin > 0) then
            begin
              A[iAdded] := S[iStr];
              Inc(iAdded);
            end else
              begin
              if LastIsSep and KeepSeparators and (iStr = iLenS) then
              begin
                A[iAdded] := S[iStr];
                Inc(iAdded);
              end;
            end;
            iBegin := iStr + 1;
            iLenSS := 0;
          end;
        end else
        begin
          if IsQuote(S[iStr]) > 0 then
          begin
            if Quoted then
            begin
              Quoted := False;
              if iStr < iLenS then
              begin
                DoubleQuoted := IsQuote(S[iStr+1]) > 0;
                if AlreadyDQ then
                  QuoteConcat := QuoteConcat + Copy(S, iBegin, iLenSS) + IfThen(DoubleQuoted, S[iStr+1], '')
                else
                  QuoteConcat := Copy(S, iBegin, iLenSS) + IfThen(DoubleQuoted, S[iStr+1], '');
                AlreadyDQ := AlreadyDQ or DoubleQuoted;
              end;
            end else
            begin
              Quoted := True;
              iBegin := iStr + 1;
              iLenSS := 0;
            end;
          end else
          begin
            if Quoted and (IsSeparator(S[iStr]) > 0) then
              Inc(iOffQuote);
            Inc(iLenSS);
          end;
        end;
      end;
     
      if iBegin <= iLenS then
      begin
        A[iAdded] := Copy(S, iBegin, MaxInt);
        Inc(iAdded);
     
        if LastIsSep and KeepSeparators then
        begin
          A[iAdded] := S[iLenS];
          Inc(iAdded);
        end;
      end;
     
      if LastIsSep and not ExcludeEmpty then
        Inc(iAdded);
     
      if iAdded < iLenA then
        A := Copy(A, 0, iAdded);
     
      Result := Result - iOffQuote;
    end;

  7. #7
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    J'ai fait des petits tests et avec les méthodes proposées (sauf celle du TStringGridCrossManager.LoadFromFile ) avec un fichier de 50Mo c'est l'erreur assurée! Mais comme dit Malatar, faut-il encore trouver des fichiers CSV de cette proportion!

    Dès que j'ai le temps, je verrai avec la TStringGridCrossManager.LoadFromFile afin d'avoir une petite opinion!

    Encore merci pour vos codes!

  8. #8
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    Euh, toute méthode de chargement dans une StringGrid sera vouée à l'echec avec de gros fichiers, puisque tout est en mémoire ...

    Sinon TStringGridCrossManager.LoadFromFile ne gère pas le CSV, c'était juste un exemple d'utilisation d'un TStringGrid pour affichage de gros fichier (mon fichier de test fait 13Mo pour 255 colonnes sur 300 lignes de 2 à 6 char, et il met 8 secondes)

  9. #9
    Modérateur
    Avatar de Rayek
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2005
    Messages
    5 235
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 235
    Points : 8 504
    Points
    8 504
    Par défaut
    En récupérant quelques astuces de Trolly () genre le ByteBool ().
    J'arrive à mettre mon fichier CSV en 1200ms-1300ms en utilisant la TStringList

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
     
    var
      lst,lstLigne: TStringList;
      i,j : integer;
      Begintime, endtime,TickPerSec : int64;
    begin
     
      if OpenDialog1.Execute then
      begin
        QueryPerformanceCounter(BeginTime);
        lst := TStringList.Create;
        lstLigne := TStringList.Create;
        try
          lst.LoadFromFile(OpenDialog1.FileName);
          ProgressBar1.Max := lst.Count div 255;
          ProgressBar1.Min := 0;
          ProgressBar1.Step := 1;
          StringGrid1.RowCount := lst.Count + 1;
          StringGrid1.ColCount := 1;
          StringGrid1.Rows[0].BeginUpdate;
          for i := 0 to lst.Count -1 do
          begin
            lstLigne.Text := lst.Strings[i];
            lstLigne.Text := StringReplace(lstLigne.text,';',#13#10,[rfReplaceAll]);
            if lstLigne.Count > StringGrid1.ColCount then
              StringGrid1.ColCount := lstLigne.count;
            for j := 0 to lstLigne.Count -1 do
              StringGrid1.Cells[j,i+1] := lstLigne.Strings[j];
            if not ByteBool(i) then
              ProgressBar1.stepit();// := i * 100 div (lst.count -1);
          end;
          StringGrid1.Rows[0].EndUpdate;
     
          QueryPerformanceCounter(EndTime);
          QueryPerformanceFrequency(TickPerSec);
          Showmessage(IntToStr(Round((EndTime - BeginTime) / TickPerSec * 1000)));
        finally
          lst.free;
          lstligne.free;
        end;
      end;
    end;

  10. #10
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut


    Je viens de tester ta méthode Malatar! Pour 17Mo, 45 secondes! On atteint pas encore les 61 secondes de mes 3.01Mo de tout à l'heure

    J'aimerai avoir 2 petites précisions.
    Si je ne me trompe pas le BeginUpdate et le EndUpdate permet d'éviter de redessiner les cellules à chaque fois ? Ainsi, c'est uniquement après le EndUpdate que toutes les cellules seront dessinées?
    Par contre, je cerne pas trop ce passage :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if not ByteBool(i) then
    ProgressBar1.stepit();
    Le ProgressBar1.stepit(); indique que l'on avance de 1 la progression, on augmente sa "capacité" ? Et le ByteBool(i) je vois pas trop ... je sens que je vais avoir une

    Merci

  11. #11
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
       with StringGridCSV.Rows[iRow] do
          begin
            BeginUpdate();
            for I := Low(ArrayCSV) to High(ArrayCSV) do
              Strings[i] := ArrayCSV[i];
            EndUpdate();
            if not ByteBool(iRow) then
               ProgressBarCSV.StepIt();
          end;
    Pour le BeginUpdate/EndUpdate, effectivement, on rempli la Rows et il rafraichit qu'à la fin
    sinon Malatar, "StringGrid1.Rows[0].EndUpdate;" j'ai trouvé cela étrange, vu que la TStringGrid et ses fonction EnsureColRow est très spécial, cela fait-il effet ? je vais tester ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if not ByteBool(iRow) then
    c'est comme, mais plus rapide

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (iRow mod 256) = 0 then
    on affiche une progression que toutes les 256 lignes ... et j'avoue qu'en dehors de la progession, je ne l'utilise pas, c'est trop peu compréhensible

  12. #12
    Modérateur
    Avatar de Rayek
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2005
    Messages
    5 235
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 235
    Points : 8 504
    Points
    8 504
    Par défaut
    Citation Envoyé par ero-sennin

    Si je ne me trompe pas le BeginUpdate et le EndUpdate permet d'éviter de redessiner les cellules à chaque fois ? Ainsi, c'est uniquement après le EndUpdate que toutes les cellules seront dessinées?
    Oui, en laissant le BeginUpdate ouvert au début, la grid reste en mode update et ne dessine pas le reste de la grid tant que le EndUpdate n'est pas appelé

    Citation Envoyé par ero-sennin
    Par contre, je cerne pas trop ce passage :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if not ByteBool(i) then
    ProgressBar1.stepit();
    Le ProgressBar1.stepit(); indique que l'on avance de 1 la progression, on augmente sa "capacité" ? Et le ByteBool(i) je vois pas trop ... je sens que je vais avoir une

    Merci
    A chaque fois que i est un multiple de 256 (255 ?) ByteBool retourne False, donc fait progresser la barre de 1 (d'où la division par 255 au début du progressbar1.max)


    [edit]

    Grillé ^^

    [/edit]

  13. #13
    Rédacteur/Modérateur
    Avatar de ero-sennin
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2005
    Messages
    2 965
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2005
    Messages : 2 965
    Points : 4 935
    Points
    4 935
    Par défaut
    Citation Envoyé par Malatar
    A chaque fois que i est un multiple de 256 (255 ?) ByteBool retourne False, donc fait progresser la barre de 1 (d'où la division par 255 au début du progressbar1.max)
    Ah ok, je cerne mieux la chose

    Citation Envoyé par ShaiLeTroll
    on affiche une progression que toutes les 256 lignes ... et j'avoue qu'en dehors de la progession, je ne l'utilise pas, c'est trop peu compréhensible
    En effet, pour la compréhension c'est pas top top mais bon, l'avoir rencontré une fois me permettra de mieux comprendre certaines sources, qui sait !

  14. #14
    Futur Membre du Club
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Septembre 2008
    Messages : 10
    Points : 9
    Points
    9
    Par défaut
    Bonjour à tous.

    J'utilise une méthode similaire à celles proposées ci-dessus mais mon fichier CSV fait maintenant 18 Meg (84732 lignes d'environ 200 charactères) que j'affiche dans un StringGrid de 10 colonnes.

    Temps de chargement (sur un i7)= 201 secondes
    152580K de mémoire occupée... alors que l'exécutable occupe moins de 2000K.

    Je crois donc qu'il est temps de changer de type de base de données !
    J'utilise Delphi 6 et ne suis pas intéressé à passer au Net framework.

    Selon vous, quel est le meilleur choix en terme de vitesse et de flexibilité ?
    ADO (que je connais bien), SQL (que je ne connais pas du tout), ou autre ?

    Merci

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [2.x] Tutoriel pour l'optimisation des performances ?
    Par vinsse2001 dans le forum Symfony
    Réponses: 5
    Dernier message: 02/05/2013, 13h32
  2. Réponses: 6
    Dernier message: 02/05/2010, 19h09
  3. Réponses: 2
    Dernier message: 24/01/2008, 13h48
  4. Votre avis sur des morceaux de resumes
    Par Asarnil dans le forum C++
    Réponses: 5
    Dernier message: 03/01/2005, 15h22
  5. Réponses: 0
    Dernier message: 10/12/2000, 12h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo