为什么数字字段在使用TFDQuery合并SQLite中的两个数据集后变为TWideStringField

为什么数字字段在使用TFDQuery合并SQLite中的两个数据集后变为TWideStringField,sqlite,delphi,delphi-10-seattle,firedac,Sqlite,Delphi,Delphi 10 Seattle,Firedac,我正在使用Delphi10,下面是示例代码 unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Datasnap.DBClient, FireDAC.Stan.Intf, FireDAC.Sta

我正在使用Delphi10,下面是示例代码

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Datasnap.DBClient,
  FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf,
  FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async,
  FireDAC.Phys, FireDAC.Phys.SQLite, FireDAC.Phys.SQLiteDef,
  FireDAC.Stan.ExprFuncs, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf,
  FireDAC.DApt, FireDAC.VCLUI.Wait, FireDAC.Comp.UI, FireDAC.Comp.Client,
  FireDAC.Phys.SQLiteVDataSet, FireDAC.Comp.DataSet, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    ClientDataSet1: TClientDataSet;
    ClientDataSet2: TClientDataSet;
    FDConnection1: TFDConnection;
    FDQuery1: TFDQuery;
    FDLocalSQL1: TFDLocalSQL;
    FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink;
    FDGUIxWaitCursor1: TFDGUIxWaitCursor;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  public
    procedure AfterConstruction; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.AfterConstruction;
var o: TFDLocalSQLDataSet;
begin
  inherited;
  ClientDataSet1.FieldDefs.Add('Code', ftString, 20);
  ClientDataSet1.FieldDefs.Add('Amount', ftFMTBcd, 2);
  ClientDataSet1.FieldDefs.Find('Amount').Precision := 18;
  ClientDataSet1.CreateDataSet;
  ClientDataSet1.AppendRecord(['A', 10]);
  ClientDataSet1.AppendRecord(['B', 20]);
  ClientDataSet1.AppendRecord(['C', 30]);

  ClientDataSet2.FieldDefs.Add('Code', ftString, 20);
  ClientDataSet2.FieldDefs.Add('Amount', ftFMTBcd, 2);
  ClientDataSet2.FieldDefs.Find('Amount').Precision := 18;
  ClientDataSet2.CreateDataSet;
  ClientDataSet2.AppendRecord(['X', 10]);
  ClientDataSet2.AppendRecord(['B', 20]);
  ClientDataSet2.AppendRecord(['Y', 30]);

  o := FDLocalSQL1.DataSets.Add;
  o.DataSet := ClientDataSet1;
  o := FDLocalSQL1.DataSets.Add;
  o.DataSet := ClientDataSet2;
  FDLocalSQL1.Active := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if FDQuery1.Active then
    FDQuery1.Close;

  FDQuery1.Open(
    'SELECT * FROM ClientDataSet1 ' +
    'UNION ' +
    'SELECT * FROM ClientDataSet2'
  );
  ShowMessage(FDQuery1.FindField('Amount').ClassName);
end;

end.
TClientDataSet的两个实例具有相同的字段结构(代码为字符串字段,金额为FMTBcd字段)


为什么FDQuery1.FindField('Amount')返回TWideStringField?

您的q首先假定您的Amount字段是数字字段,但我认为Sqlite实际上不是这样工作的

Sqlite列不是严格类型的,FireDAC告诉您所有Sqlite列实际上是什么,即WideString,即使您将列声明为其他类型。在Sqlite中,将列定义为特定类型(f.i.auto incrementing integer)更像是该列中数据的行为,而不是存储方式

Fwiw、FireDAC在理解Sqlite列及其元数据方面总体上似乎比DBExpress做得更好,但您仍然会得到像您所问的那样的“乐趣”


显然(请参阅下面权威机构的评论)如果SqlLite向FireDAC提供列类型名称,那么FireDAC将尝试使用列中出现的第一个值的实际数据类型。如果为Null,FireDAC将使用ftWideString作为列类型。

基本正确,但添加了一项。若SQLite确实向FireDAC提供了列类型名称,比如UNION,那个么FireDAC将尝试使用列第一个值的实际数据类型。如果为NULL,则FireDAC将列定义为ftWideString。