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

Why numeric field become TWideStringField after union two datasets in SQLite using TFDQuery

我正在使用 Delphi 10 Seattle 以下是示例代码。

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') return TWideStringField?

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

Sqlite 列不是严格类型化的,FireDAC 会告诉您所有 Sqlite 列的真正含义,即 WideStrings,即使您将列声明为其他类型。在 Sqlite 中,将列定义为特定类型(f.i。自动递增整数)更多的是该列中数据的行为方式而不是存储方式的问题。

Fwiw,一般来说,FireDAC 在理解 Sqlite 列及其元数据方面似乎比 DBExpress 做得更好,但你仍然得到 "funnies" 就像你所询问的那样。

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