当另一个字段数据不存在时清除一个字段
Posted
技术标签:
【中文标题】当另一个字段数据不存在时清除一个字段【英文标题】:Clear a field when another field data doesn't exist 【发布时间】:2017-08-27 18:09:01 【问题描述】:我有一个名为“refFile”的字段,其中可能有也可能没有路径描述。
我需要遍历整个数据库并检查“refFile”中定义的路径是否仍然存在。
使用 Delphi (Pascal) 这需要很多很多分钟
bigDB.First;
while not(bigDB.EOF) do
begin
if Trim(bigDB.FieldByName('refFile').AsString) > '' then
begin
if not(FileExists(bigDB.FieldByName('refFile').AsString)) then
begin
bigDB.Edit;
bigDB.FieldByName('refFile').AsString:='';
bigDB.Post;
end;
end;
bigDB.Next;
end;
我如何在 SQL 中做到这一点?
谢谢。
【问题讨论】:
您使用的组件是什么? FireDAC、ADO、dbExpress..?首先要改进的是缓存字段引用(摆脱重复地按名称询问字段)。接下来仅取决于,所有文件都在同一目录下吗?或者任何驱动器上的任何地方?我的意思是,是否值得为此操作构建文件树?接下来可能是在不移动光标的情况下对数据集进行快速迭代。因此,例如检查数据集中的内容,例如,您是否没有在此数据集中存储和获取 blob。 为什么您实际上需要“提前”了解这些信息?文件存在可以经常改变.. 【参考方案1】:您无法在 SQLLite 中检查路径的有效性,但您可以过滤路径中包含某些内容的记录并减少要检查的行列表。
您可以对该字段的记录进行排序(如果您有索引)并仅检查您之前未检查的路径。
您也可以使用线程在后台执行此长时间操作。只需使用 TThread.Createanonymousthread(procedure begin end).Start;
【讨论】:
最好的解决方案是过滤SQL中的记录(order + where),在Delphi中只检查以前没有检查过的路径。使用线程许可做后台工作,不要冻结应用程序。 @Victoria - 没错。我的经验是,如果您偏离问题并尝试解决 实际 问题,那么问题就会丢失,提问者(即使对于新手来说也是如此)会产生这样的印象,即您必须解决没有被问到的问题.我的观点是,我们应该鼓励提出更好的问题。 @Victoria - FWIW,我希望您不会考虑放弃 SO,因为我认为您通过输入和 cmets 带来了很多价值。 @Victoria:“FWIW,我希望你考虑不要放弃 SO” 我也是,我认为你在这里的贡献是无价的。 @Victoria:顺便说一句,我认为这个 q 和所有 cmets 只是表明,不充分的问题产生的热量多于光线。所以,我做了我希望更多人首先做的事情,即投票关闭,因为在它的上下文中,q 不够清楚,无法正确回答。【参考方案2】:您无法在普通的 SQLLite 查询中检查文件是否存在。您可以使用UDF (User defined function) 来做到这一点,但它会稍微复杂一些,并且需要一些其他编程语言的技能(请注意,在这种情况下,您的文件应该可以从服务器访问,否则它将无法工作)。
如果您正在寻找更简单的解决方案,我认为您可以通过减少查询产生的记录数并改进您的 Delphi 代码来加快程序速度更高效。
选择 SQL:
使用length 和trim 函数可以减少Delphi 代码要验证的记录数。
select refFile
from myTable
where (refFile is not null) and (length(trim(refFile)) > 0)
德尔福:
只拨打一次TDataSet.FieldByName。尝试使用TDataSet.DisableControls和TDataSet.EnableControls(这样一些数据集的组件会更快,即使数据集组件没有链接到任何控件)。
var
Fld : TField;
begin
BigDB.DisableControls();
try
Fld := BigDB.FieldByName('refFile');
BigDB.First;
while not(BigDB.Eof) do
begin
if not(FileExists(Fld.AsString)) then
begin
BigDB.Edit;
Fld.AsString := '';
BigDB.Post;
end;
BigDB.Next;
end;
finally
BigDB.EnableControls();
end;
此外,您可以考虑以下其他优化:
如果refFile
字段多次包含相同的值,您可以按refFile
字段对查询进行排序并更改Delphi 代码以便只验证每个文件名一次。 (您可以通过存储FileExists
函数的最后一个值和结果来做到这一点)。
您可以使用TThread 类异步运行您的代码。这样您的应用程序就不会死机,而且速度会更快。
【讨论】:
想一想,通常数据库引擎甚至不会在客户端运行。 UDF 如何帮助过滤服务器端的客户端文件?虽然我怀疑这是否会成为您投反对票的原因,但看起来投反对票的人天生就已经越界了。 @SertacAkyuz:你说得对,我认为文件也可以从服务器访问是理所当然的。我通过指定这个事实更新了答案的第一部分。 你也可以在Delphi中注册UDF。这是关于导入一些 C 函数。您的客户端库只需要为打开的数据库提供句柄。 @exd - 谢谢。另请注意,客户端必须通过共享存储路径。 IE。如果数据库服务器不在同一台机器上,“c:\..something”对服务器没有任何意义。【参考方案3】:例如使用 FireDAC,创建用户定义的函数非常容易。如果您正在使用它,请尝试类似的方法。它可以节省一些时间,因为引擎不需要将结果集获取到客户端应用程序:
uses
FireDAC.Phys.SQLiteWrapper;
type
TForm1 = class(TForm)
Button1: TButton;
FDQuery1: TFDQuery;
FDConnection1: TFDConnection;
FDGUIxWaitCursor1: TFDGUIxWaitCursor;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
FValidator: TSQLiteFunction;
procedure ValidateFile(AFunc: TSQLiteFunctionData; AInputs: TSQLiteInputs;
AOutput: TSQLiteOutput; var AUserData: TObject);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FDConnection1.Open;
FValidator := TSQLiteFunction.Create((TObject(FDConnection1.CliObj) as TSQLiteDatabase).Lib);
FValidator.Args := 1;
FValidator.Name := 'FileExists';
FValidator.OnCalculate := ValidateFile;
FValidator.InstallAll;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FDQuery1.SQL.Text :=
'UPDATE MyTable SET FileName = NULL WHERE ' +
'FileName IS NOT NULL AND NOT FileExists(FileName)';
FDQuery1.ExecSQL;
end;
procedure TForm1.ValidateFile(AFunc: TSQLiteFunctionData; AInputs: TSQLiteInputs;
AOutput: TSQLiteOutput; var AUserData: TObject);
begin
AOutput.AsBoolean := FileExists(AInputs[0].AsString);
end;
或者干脆删除TFDSQLiteFunction组件,用函数名称填写FunctionName属性,编写类似于上面的OnCalculate事件处理程序,并通过设置Active属性启用组件。
【讨论】:
纯属推测,但我猜这会产生额外的减慢效果,因为假设数据库服务器位于不同的盒子上,对于每条记录,客户端都需要额外的往返。 @Sertac,我假设只在本地使用(抱歉,这篇文章是对使用 FireDAC 的猜测,所以我没有提到远程机器使用的可能性)。通过本地使用,您可能会获得一些加速,因为将整个文件名结果集获取到应用程序、检查是否存在并仅更新不存在的文件元组应该比 UDF 回调更昂贵。以上是关于当另一个字段数据不存在时清除一个字段的主要内容,如果未能解决你的问题,请参考以下文章