尝试刷新连接到SQL语句中具有联接表的数据集的ClientDataset上的记录时,TClientDataset.RefreshRecord不再生成SQL的表连接部分.
因此,调用此方法会导致SQL错误"无效列名称"为不在主表中的每个字段.
这在Delphi 2010及更早版本中不是问题.
连接到TClientDataset的DBX4或BDE组件都会发生错误,因此很可能是TClientDataset代码更改出现问题.
要复制此问题:
在Delphi XE中创建一个只有一个表单的新应用程序,并在其上删除所需的数据库组件(TSQLMonitor,TSQLConnection,TSQLQuery,TDatasetProvider,TClientDataset,TDatasource和TDBGrid)并将它们相互绑定.
使用表连接创建一个简单的SQL语句,并将其放在TSQLDataset.SQL属性中.
SQL语句只包含两个字段 - 主表的键字段和连接表中的字段 - 例如伪代码:
Select
MainTable.IntegerKeyField
, JoinedTable.JoinField
FROM MainTable
LEFT OUTER JOIN JoinedTable ON MainTable.LookupFieldID = JoinedTable.JoinKeyField
Run Code Online (Sandbox Code Playgroud)
将这两个字段添加为TSQLQuery和TClientDataset中的持久字段,其中包含pfInKey的关键字段的Provider Flag(如果不知道哪个字段是密钥,则RefreshRecord将不起作用,因此必须使用持久字段).
在表单上添加两个按钮 - 一个只打开Clientdataset,第二个按钮调用clientdataset.refreshrecord;
运行应用程序,按下按钮打开网格中的数据集和数据显示.
按"刷新记录"按钮,您将收到连接字段的SQL错误"无效列名称".
关闭应用程序,打开SQLMonitor日志并在Delphi生成的刷新记录SQL语句中,您将看到它没有包含表连接语句.
====
我真的很感激有关如何解决这个问题的任何想法.
我上周观察到了一些我没想到的事情,并将在下面描述.我很好奇为什么会这样.它是TDataSet类内部的东西,TDBGrid的工件还是别的东西?
打开的ClientDataSet中的字段顺序已更改.具体来说,我在使用FieldDefs定义其结构后通过调用CreateDatatSet在代码中创建了一个ClientDataSet.此ClientDataSet结构中的第一个字段是名为StartOfWeek的Date字段.不久之后,我编写的代码(假设StartOfWeek字段位于零位置,ClientDataSet.Fields [0])失败,因为StartOfWeek字段不再是ClientDataSet中的第一个字段.
经过一番调查后,我了解到ClientDataSet中的每个字段都可能在给定时刻出现在创建ClientDataSet时与原始结构不同的某个位置.我不知道这可能会发生,并且对谷歌的搜索也没有提到这种效果.
发生了什么不是魔术.这些字段本身并没有改变位置,也没有根据我在代码中所做的任何事情而改变.导致字段在物理上看起来改变ClientDataSet中位置的原因是用户已经更改了ClientDataSet附加到的DbGrid中的列的顺序(当然是通过DataSource组件).我在Delphi 7,Delphi 2007和Delphi 2010中复制了这个效果.
我创建了一个非常简单的Delphi应用程序来演示这种效果.它由一个包含一个DBGrid,一个DataSource,两个ClientDataSets和两个Buttons的表单组成.此表单的OnCreate事件处理程序如下所示
procedure TForm1.FormCreate(Sender: TObject);
begin
with ClientDataSet1.FieldDefs do
begin
Clear;
Add('StartOfWeek', ftDate);
Add('Label', ftString, 30);
Add('Count', ftInteger);
Add('Active', ftBoolean);
end;
ClientDataSet1.CreateDataSet;
end;
Run Code Online (Sandbox Code Playgroud)
Button1,标记为Show ClientDataSet Structure,包含以下OnClick事件处理程序.
procedure TForm1.Button1Click(Sender: TObject);
var
sl: TStringList;
i: Integer;
begin
sl := TStringList.Create;
try
sl.Add('The Structure of ' + ClientDataSet1.Name);
sl.Add('- - - - - - - - - - - - - - - - - ');
for i := 0 to ClientDataSet1.FieldCount - 1 do
sl.Add(ClientDataSet1.Fields[i].FieldName); …Run Code Online (Sandbox Code Playgroud) 我有一个自定义的TDatasetProvider,允许为它提供的任何数据创建新的字段.
所以,假设您在原始数据集上获得了以下字段:
您需要使用显示位图在DBGrid上选择它.好吧,你可以,因为我的DSP可以添加一个调用Selected数据集数据的布尔字段.
我现在这样做的方式:
我真的不知道是否有更优雅(和更快)的方式来做到这一点.有另一种(更快和/或更优雅)的方式来获得这个结果?
我在一些客户端声明MidasLib以避免由Midas.dll引起的dll地狱.
下面的代码运行大约2350ms.如果我在使用中删除了MidaLib声明,它将在45毫秒内开始运行!!
data.xml文件使用TClientDataSet.SaveToFile方法保存,有5000条记录,大小约为600Kb.
有谁知道如何解释这种奇怪的行为?
我可以在Delphi XE2 upd 3和Delphi XE3 upd 2中确认问题.
谢谢.
program Loader;
{$APPTYPE CONSOLE}
{$R *.res}
uses
MidasLib,
System.SysUtils,
Winapi.Windows,
Data.DB,
Datasnap.DBClient;
var
cds : TClientDataSet;
start, stop : Cardinal;
begin
cds := TClientDataSet.Create(nil);
try
start := GetTickCount;
cds.LoadFromFile('c:\temp\data.xml');
stop := GetTickCount;
Writeln(Format('Time elapsed: %dms', [stop-start]));
finally
cds.Free;
end;
end.
Run Code Online (Sandbox Code Playgroud) 我有一个在线程1中管理的TClientDataset.
在另一个线程中,我有一个克隆的TClientDataset图像.
我会遇到线程问题吗?
编辑
克隆的图像以只读模式使用.
在我正在工作的项目中,我有如下代码.
procedure TForm.EditBtnClick(Sender:TObject);
begin
// Mark is form variable. It's private
Mark = cdsMain.GetBookmark;
// blabalbal
.
.
.
end;
procedure TForm.OkBtnClick(Sender:TObject);
var
mistakes: Integer;
begin
//Validation stuff and transaction control
//removed to not clutter the code
If cdsMain.ChangeCount <> 0 then
mistakes := cdsMain.AppyUpdates(-1);
cdsMain.Refresh;
try
cdsMain.GotoBookmark(Mark);
// Yes, I know I would have to call FreeBookmark
// but I'm just reproducing
except
cdsMain.First;
end;
end;
Run Code Online (Sandbox Code Playgroud)
就个人而言,我没有太多使用书签 - 除了重新定位我只移动光标位置的数据集(创建列表,填充字符串列表等).如果我Refresh,更新(特别是当过滤器可以使记录不可见),重新获取(Close/ Open)或修改数据集中的数据的任何操作时,我不使用书签.我更喜欢Locate使用主键(TClientDataset当然使用a …
我有ClientDatSet一些fkInternalCalc领域.CDS与任何提供商均无关联; 而是它在飞行中填充.如何强制CDS重新计算所有"可计算"字段?我无法打电话,Refresh()因为没有提供商来刷新数据.我到目前为止唯一的方法是浏览所有记录,这不是最好的方法.
我知道您无法完全描述具有XSD架构的TClientDataSet的XML,因为ROW元素具有名称随内容而变化的属性.
但是,这种XML的METADATA部分应该是.
所以:有具有(部分)的XSD描述了可以用Delphi TClientDataSets被保存在XML的元数据部分的人?
问候.
PS:
感谢指向XML-> XSD转换工具/站点; 我本来应该写的,我自己也这样做了,但是以适当的方式生成XSD(即涵盖所有可能性的那个)将需要输入XML,涵盖所有可能性(如往返,rowstate等).我会尝试以这种方式提出一个像样的XSD并在此发布.
有没有办法只刷新Detail DataSet而不重新加载所有主数据集?
这是我到目前为止所尝试的:
DM.ClientDataSet2.Refresh;
DM.ClientDataSet2.RefreshRecord;
Run Code Online (Sandbox Code Playgroud)
我也尝试过:
DM.ClientDataSet1.Refresh;
Run Code Online (Sandbox Code Playgroud)
但是上面的方法会刷新整个Master数据集,而不仅仅是当前记录.
现在,以下代码似乎做了什么:
DM.ClientDataSet1.RefreshRecord;
Run Code Online (Sandbox Code Playgroud)
是否有解决方法或正确的方法来做我想要的?(也许是插入......)
附加信息:
ClientDataSet1 =主数据集
ClientDataSet2 = Detail DataSet,如下:*
object ClientDataSet2: TClientDataSet
Aggregates = <>
DataSetField = ClientDataSet1ADOQuery2
FetchOnDemand = False
.....
end
Run Code Online (Sandbox Code Playgroud)
提供者属性:
object DataSetProvider1: TDataSetProvider
DataSet = ADOQuery1
Options = [poFetchDetailsOnDemand]
UpdateMode = upWhereKeyOnly
Left = 24
Top = 104
end
Run Code Online (Sandbox Code Playgroud)