使用BCP将SQL表转为delphi记录

发布于 2024-12-22 10:47:05 字数 1330 浏览 4 评论 0原文

我有一个场景,我必须从 SQL 表中导出大约 500,000 条记录的数据,以便在 Delphi 应用程序中使用。数据将被加载到打包记录中。有没有一种方法可以使用 BCP 写入数据文件,类似于将记录写入文件。

截至目前,我正在使用此伪代码加载数据。

    // Assign the data file generated from BCP to the TextFile object.
    AssignFile(losDataFile, loslFileName);
    Reset(losDataFile);
    while not EOD(losDataFile) do
    begin
      // Read from the data file until we encounter the End of File
      ReadLn(losDataFile, loslDataString);

      // Use the string list comma text to strip the fields
      loclTempSlist.CommaText := loslDataString;

      // Load the record from the items of the string list.
      DummyRec.Name := loclTempSList[0];
      DummyRec.Mapped = loclTempSList[1] = 'Y';
    end;

为了方便起见,我在下面列出了虚拟记录的类型

    TDummyRec = packed record
      Name : string[255];
      Mapped : Boolean;
    end;

所以,我的问题是,是否可以将数据导出到二进制文件,而不是将数据导出到文本文件,以便我可以使用记录类型直接从文件中读取数据?

就像

   loclFileStream := TFileStream.Create('xxxxxx.dat', fmOpenRead or fmShareDenyNone);
   while loclFileStream.Position < loclFileStream.Size do
   begin
     // Read from the binary file
     loclFileStream.Read(losDummyData, SizeOf(TDummyRec));
     //-  -------- Do wat ever i want.
   end;

我没有太多使用 BCP 的经验一样。请帮我解决这个问题。

谢谢 终结者...

I have a scenario in which I have to export data of around 500,000 records from sql table to be used in Delphi application. The data is to be loaded into a packed record. Is there a method in which i can use the BCP to write data file similar to that of writing the records to file.

As of now I am loading the data using this psudo code.

    // Assign the data file generated from BCP to the TextFile object.
    AssignFile(losDataFile, loslFileName);
    Reset(losDataFile);
    while not EOD(losDataFile) do
    begin
      // Read from the data file until we encounter the End of File
      ReadLn(losDataFile, loslDataString);

      // Use the string list comma text to strip the fields
      loclTempSlist.CommaText := loslDataString;

      // Load the record from the items of the string list.
      DummyRec.Name := loclTempSList[0];
      DummyRec.Mapped = loclTempSList[1] = 'Y';
    end;

For convenience i have listed the type of Dummy rec below

    TDummyRec = packed record
      Name : string[255];
      Mapped : Boolean;
    end;

So, my question is, instead of exporting the data to a text file, will it be possible to export the data to binary so that i can read from the file directly using the record type?

like

   loclFileStream := TFileStream.Create('xxxxxx.dat', fmOpenRead or fmShareDenyNone);
   while loclFileStream.Position < loclFileStream.Size do
   begin
     // Read from the binary file
     loclFileStream.Read(losDummyData, SizeOf(TDummyRec));
     //-  -------- Do wat ever i want.
   end;

I don't have much experience on using the BCP. Please help me with this.

Thanks
Terminator...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

九歌凝 2024-12-29 10:47:05

在您的记录中,string[255] 将创建一个固定大小的 Ansi 字符串(即所谓的shortstring)。这种类型显然已被弃用,并且不应在您的代码中使用。

使用 TFileStream 直接保存它会非常浪费空间(即使它可以工作)。每个记录将为每个名称存储 256 个字节。

使用string[255](即所谓的shortstring)将对字符串进行隐藏转换,以便大多数访问它。所以这不是最好的选择,恕我直言。

我的建议是 使用动态数组,然后使用我们的开源类对其进行序列化/反序列化。对于您的存储,您可以使用动态数组。适用于 Delphi 5 至 XE2。您将能够在记录中使用字符串

TDummyRec = packed record
  Name : string; // native Delphi string (no shortstring)
  Mapped : Boolean;
end;

在OP评论后编辑:

BCP只是一个用于导出的命令行工具> SQL 表中很多行。所以恕我直言,BCP 不适合您的目的。

您似乎需要从 SQL 表导入很多行。

在这种情况下:

  • 使用 shortstring 在任何情况下都会浪费内存,因此与使用好的 string 相比,您会更快地耗尽内存;
  • 您可以尝试我们的开源类逐一检索所有数据行,然后使用这些数据填充您的记录:请参阅 SynDB 类 - 它比 ADO 更轻;然后,您将能够一一检索记录数据,然后使用我们的记录序列化函数创建一些二进制内容 - 或者尝试使用专用的更快引擎,例如我们的 SynBigTable;
  • 有一些关于直接使用 Delphi 代码中的 BCP 使用的 OleDB 功能的文章这里 - 它是法语,但您可以使用谷歌翻译它此处用于快速批量复制;包括完整的源代码。

In your record, a string[255] will create a fixed-size Ansi string (i.e. a so-called shortstring). This type is clearly deprecated, and should not be used in your code.

It will be an awful waste of space to save it directly, using a TFileStream (even if it will work). Each record will store 256 bytes for each Name.

And using a string[255] (i.e. a so-called shortstring) will make an hidden conversion to a string for most access to it. So it is not the best option, IMHO.

My advice is to use a dynamic array then serialize / unserialize it with our Open Source classes. For your storage, you can use a dynamic array. Works from Delphi 5 up to XE2. And you'll be able to use a string in the record:

TDummyRec = packed record
  Name : string; // native Delphi string (no shortstring)
  Mapped : Boolean;
end;

Edit after OP's comment:

BCP is just a command-line tool meant to export a lot of rows into a SQL table. So IMHO BCP is not the good candidate for your purpose.

You seems to need to import a lot of rows from a SQL table.

In this case:

  • Using shortstring will be in all case a waste of memory, so you'll get faster out of memory than with using a good string;
  • You can try our Open Source classes to retrieve all data rows one by one, then populate your records using this data: see SynDB classes - it is lighter than ADO; Then you'll be able to retrieve the record data one by one, then use our record serialization functions to create some binary content - or try a dedicated faster engine like our SynBigTable;
  • There are some articles about using directly the OleDB feature used by BCP from Delphi code in here - it is in french, but you can use google to translate it and here for fast bulk copy; full source code included.
难如初 2024-12-29 10:47:05

你想将 SQL 表读入记录,我不知道你为什么要使用古老的分配文件。

您确实应该为您的数据库使用 TADOQuery(或合适的变体)。
在其中放入合理的 SQL 查询;类似:

SELECT field1, field2, field3 FROM tablename WHERE .....

如有疑问,您可以使用:

SELECT * FROM tablename

这将从表中选择所有字段。

以下代码将遍历所有记录和所有字段,并将它们保存在变体中,并将​​其保存在 FileStream 中。

function NewFile(Filename: string): TFileStream;
begin
  Result:= TFileStream.Create(Filename, fmOpenWrite);
end;

function SaveQueryToFileStream(AFile: TFileStream; AQuery: TADOQuery): boolean;
const
  Success = true;
  Failure = false;
  UniqueFilePrefix = 'MyCustomFileTypeId';
  BufSize = 4096;
var
  Value: variant;
  Writer: TWriter;
  FieldCount: integer;
  c: integer;
  RowCount: integer;
begin
  Result:= Success;
  try
    if not(AQuery.Active) then AQuery.Open

    FieldCount:= AQuery.Fields.Count;
    Writer:= TWriter.Create(AFile, BufSize);
    try
      Writer.WriteString(UniqueFilePrefix)
      //Write the record info first
      Writer.WriteInteger(FieldCount);
      //Write the number of rows
      RowCount:= AQuery.RecordCount;
      WriteInteger(RowCount);
      AQuery.First;
      while not(AQuery.eof) do begin
        for c:= 0 to FieldCount -1 do begin
          Value:= AQuery.Fields[c].Value;
          Writer.WriteVariant(Value);
        end; {for c}
        AQuery.Next;
      end; {while}
    except 
      Result:= failure;
    end;
  finally
    Writer.Free;
  end;
end;

You want to read a SQL-table into a record, I have no idea why you are working with the archaic AssignFile.

You should really use a TADOQuery (or suitable variant) for you database.
Put a sensible SQL-query in it; something like:

SELECT field1, field2, field3 FROM tablename WHERE .....

When in doubt you can use:

SELECT * FROM tablename

Which will select all fields from the table.

The following code will walk through all the records and all the fields and save them in a variants and save that in a FileStream.

function NewFile(Filename: string): TFileStream;
begin
  Result:= TFileStream.Create(Filename, fmOpenWrite);
end;

function SaveQueryToFileStream(AFile: TFileStream; AQuery: TADOQuery): boolean;
const
  Success = true;
  Failure = false;
  UniqueFilePrefix = 'MyCustomFileTypeId';
  BufSize = 4096;
var
  Value: variant;
  Writer: TWriter;
  FieldCount: integer;
  c: integer;
  RowCount: integer;
begin
  Result:= Success;
  try
    if not(AQuery.Active) then AQuery.Open

    FieldCount:= AQuery.Fields.Count;
    Writer:= TWriter.Create(AFile, BufSize);
    try
      Writer.WriteString(UniqueFilePrefix)
      //Write the record info first
      Writer.WriteInteger(FieldCount);
      //Write the number of rows
      RowCount:= AQuery.RecordCount;
      WriteInteger(RowCount);
      AQuery.First;
      while not(AQuery.eof) do begin
        for c:= 0 to FieldCount -1 do begin
          Value:= AQuery.Fields[c].Value;
          Writer.WriteVariant(Value);
        end; {for c}
        AQuery.Next;
      end; {while}
    except 
      Result:= failure;
    end;
  finally
    Writer.Free;
  end;
end;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文