Delphi TQuery save to csv file

34.6k views Asked by At

I want to export content of a TQuery to a CSV file without using a 3d part component(Delphi 7). From my knowledge this can not be accomplished with Delphi standard components.

My solution was to save the content in a StringList with a CSV format, and save it to a file.

Is there any comfortable solution?

PS:I don't want to use JvCsvDataSet or any component. Question is: can this be accomplished only with Delphi 7 or higher standard components?

Thank you in advance!

5

There are 5 answers

2
Ken White On BEST ANSWER

Of course it can.

You just have to do the work to properly output the CSV content (quoting properly, handling embedded quotes and commas, etc.). You can easily write the output using TFileStream, and get the data using the TQuery.Fields and TQuery.FieldCount properly.

I'll leave the fancy CSV quoting and special handling to you. This will take care of the easy part:

var
  Stream: TFileStream;
  i: Integer;
  OutLine: string;
  sTemp: string;
begin
  Stream := TFileStream.Create('C:\Data\YourFile.csv', fmCreate);
  try
    while not Query1.Eof do
    begin
      // You'll need to add your special handling here where OutLine is built
      OutLine := '';
      for i := 0 to Query.FieldCount - 1 do
      begin
        sTemp := Query.Fields[i].AsString;
        // Special handling to sTemp here
        OutLine := OutLine + sTemp + ',';
      end;
      // Remove final unnecessary ','
      SetLength(OutLine, Length(OutLine) - 1);
      // Write line to file
      Stream.Write(OutLine[1], Length(OutLine) * SizeOf(Char));
      // Write line ending
      Stream.Write(sLineBreak, Length(sLineBreak));
      Query1.Next;
    end;
  finally
    Stream.Free;  // Saves the file
  end;
end;
0
Adam Henderson On

Sorry to reply to a seven year old question but if anyone stumbles upon it (as I did) and doesn't want to implement and maintain their own CSV writer, the following code demonstrates how to use FireDAC components (which come installed with Delphi since XE5) to save a dataset to CSV in just a few basic steps:

uses
  FireDAC.Comp.BatchMove,
  FireDAC.Comp.BatchMove.Text,
  FireDAC.Comp.BatchMove.DataSet;

procedure SaveDatasetToCSV(IncludeFieldNames : Boolean; Dataset : TDataset; Filename : String);
var
  TextWriter : TFDBatchMoveTextWriter;
  DataSetReader : TFDBatchMoveDataSetReader;
  BatchMove : TFDBatchMove;
begin
  BatchMove := nil;
  try
    BatchMove := TFDBatchMove.Create(nil); 
    TextWriter := TFDBatchMoveTextWriter.Create(BatchMove); 
    DataSetReader := TFDBatchMoveDataSetReader.Create(BatchMove); 

    DataSetReader.DataSet := Dataset;

    TextWriter.FileName := Filename;
    TextWriter.DataDef.WithFieldNames := IncludeFieldNames;

    BatchMove.Reader := DataSetReader;
    BatchMove.Writer := TextWriter; 
    BatchMove.Options := BatchMove.Options + [poClearDest]; //Overrides file if it already exists
    BatchMove.Execute;
  finally
    BatchMove.Free;
  end;
end;
2
Rob McDonell On

The original question asked for a solution using a StringList. So it would be something more like this. It will work with any TDataSet, not just a TQuery.

procedure WriteDataSetToCSV(DataSet: TDataSet, FileName: String);
var
  List: TStringList;
  S: String;
  I: Integer;
begin
  List := TStringList.Create;
  try
    DataSet.First;
    while not DataSet.Eof do
    begin
      S := '';
      for I := 0 to DataSet.FieldCount - 1 do
      begin
        if S > '' then
          S := S + ',';
        S := S + '"' + DataSet.Fields[I].AsString + '"';
      end;
      List.Add(S);
      DataSet.Next;
    end;
  finally
    List.SaveToFile(FileName);
    List.Free;
  end;
end;

You can add options to change the delimiter type or whatever.

0
Didier Cabalé On

Delphi does not provide any built-in access to .csv data. However, following the VCL TXMLTransform paradigm, I wrote a TCsvTransform class helper that will translate a .csv structure to /from a TClientDataSet. As for the initial question that was to export a TQuery to .csv, a simple TDataSetProvider will make the link between TQuery and TClientDataSet. For more details about TCsvTransform, cf http://didier.cabale.free.fr/delphi.htm#uCsvTransform

0
Giorgio Calzolato On

This is like the Rob McDonell solution but with some enhancements: header, escape chars, enclosure only when required, and ";" separator. You can easily disable this enhancements if not required.

procedure SaveToCSV(DataSet: TDataSet; FileName: String);
const
  Delimiter: Char = ';'; // In order to be automatically recognized in Microsoft Excel use ";", not ","
  Enclosure: Char = '"';
var
  List: TStringList;
  S: String;
  I: Integer;
  function EscapeString(s: string): string;
  var
    i: Integer;
  begin
    Result := StringReplace(s,Enclosure,Enclosure+Enclosure,[rfReplaceAll]);
    if (Pos(Delimiter,s) > 0) OR (Pos(Enclosure,s) > 0) then  // Comment this line for enclosure in every fields
        Result := Enclosure+Result+Enclosure;
  end;
  procedure AddHeader;
  var
    I: Integer;
  begin
    S := '';
    for I := 0 to DataSet.FieldCount - 1 do begin
      if S > '' then
        S := S + Delimiter;
      S := S + EscapeString(DataSet.Fields[I].FieldName);
    end;
    List.Add(S);
  end;
  procedure AddRecord;
  var
    I: Integer;
  begin
    S := '';
    for I := 0 to DataSet.FieldCount - 1 do begin
      if S > '' then
        S := S + Delimiter;
      S := S + EscapeString(DataSet.Fields[I].AsString);
    end;
    List.Add(S);
  end;
begin
  List := TStringList.Create;
  try
    DataSet.DisableControls;
    DataSet.First;
    AddHeader;  // Comment if header not required
    while not DataSet.Eof do begin
      AddRecord;
      DataSet.Next;
    end;
  finally
    List.SaveToFile(FileName);
    DataSet.First;
    DataSet.EnableControls;
    List.Free;
  end;
end;