我从您的问题中了解到,您目前正试图检查数据库中是否存在特定记录。我会非常简短地回答这个问题,因为这个网站上有很多类似的问题可以帮助你充实细节。
但是,您的问题标题询问“交叉引用数据写入具有现有数据库的文本文件”。从描述中听起来好像您正在尝试协调来自两个来源的数据并找出匹配的内容和不匹配的内容。我会花更多时间来回答这个问题,因为我认为会有更多有价值的信息。
要检查数据库中的数据,您需要:
- 您配置为指向您的数据库的连接组件。
- 链接到连接组件的查询组件。
- 查询文本将使用 SQL 语句从数据库中的特定表中选择行。
- 我建议您对查询进行参数化,以专门选择您要查找的行(我稍后会解释原因。)
-
注意:您可以使用表格组件而不是查询组件,这将改变您检查现有行的方式。它的优点是您不需要编写 SQL 代码。但编写良好的 SQL 将更具可扩展性。
以上选项因您使用的数据库和组件而异。但正如我所说,已经有很多类似的问题。通过一些研究,您应该能够弄清楚。
如果您遇到困难,您可以提出更具体的问题,详细说明您尝试过的方法和无效的方法。 (请记住,这不是免费的“为你服务”,如果看起来像你所期望的那样,你会受到强烈反对。)
在文本文件和数据库之间协调数据:
有几种不同的方法。你选择的那个是完全可以接受的。基本上可以归结为:
- 对于
TheFile 中的每个Entry
- .. 如果
Entry 存在于TheDatabase
- .. .. 用
Entry 做点什么
- .. .. 否则用
Entry 做其他事情
上述步骤很容易理解,因此很容易确信算法是正确的。 Delphi 中是否没有单行代码来实现这些步骤并不重要。作为程序员,您有权创建所需的任何其他功能/程序。
保持例程的结构简单很重要。
上述任何步骤都不能非常简单地实现,然后您需要分解成更小的步骤:2.a. 2.b。 ; 3.a. 3.b。 3.c。 ;等等(这就是自上而下的设计。)
提示:您希望将所有不同的细分转换为它们自己的函数和过程。这将使维护您的程序和重用您已经编写的例程变得更加容易。
我将专注于分解第 2 步。如果您的数据库和文本文件变得非常大,那么如何执行此操作可能非常重要。例如,您可以这样实现:每次调用该函数来检查“Entry 是否存在”时,它都会查看数据库中的每条记录。这将非常糟糕,因为如果您的文件中有 m 条目并且数据库中有 n 条目,那么您将执行 m x n 检查。
记得我说过我会解释为什么我建议使用参数化查询?
数据库的设计和编写是为了管理数据。存储和检索数据是它们的主要功能,因此让它来完成查找您要查找的条目是否存在的工作。例如,如果您编写查询以将所有条目提取到您的 Delphi 应用程序中并在那里搜索:
- 增加应用程序的内存需求。
- 但更重要的是,无需额外的工作,让自己暴露在上面提到的
m x n 问题中。
使用参数化查询,每次调用if EntryExists(...) 时,您都可以更改参数值并有效地要求数据库查找记录。数据库完成工作,并为您提供答案。因此,例如,您可以将函数编写如下:
function TForm1.EntryExists(const AName: string): Boolean;
begin
qryFindEntry.Close;
qryFindEntry.Parameters.ParamByName('EntryName').Value := AName;
qryFindEntry.Open;
Result := qryFindEntry.RecordCount > 0;
end;
提示:在数据库中的适当列上定义索引非常重要,否则每次打开查询时,它也会搜索每条记录。
注意:另一个非常相似的选项是在数据库上编写存储过程,并使用存储过程组件来调用数据库。
其他 cmets:
您处理文件的例程被硬编码为使用register.txt
这使得它在当前形式下不可重用。而是将代码移动到一个单独的方法中:procedure ProcessFile(AFileName: string);。然后在您的按钮单击事件处理程序中调用:ProcessFile('register.txt');。
提示:事实上,将大量代码从事件处理程序中移出到带有适当参数的方法中通常是个好主意。更改您的事件处理程序以调用这些方法。这样做将使您的代码更易于维护、测试和重用。
你的异常处理有误
这是一种非常糟糕的异常处理方式。
首先,您永远不想编写不必要的异常处理。它只会使您的代码膨胀,使其更难以阅读和维护。引发异常时:
- 程序开始将代码退出到最里面的 finally/except 块。 (因此异常会退出您的例程 - 正如您添加的代码一样。)
- 默认情况下,应用程序异常处理程序将处理未处理的异常(意味着您尚未在某处吞下的异常)。默认情况下,这只会显示一个错误对话框。 (正如您添加的代码一样。)
- 您的代码所做的唯一更改是显示与实际引发的消息不同的消息。问题是您做出了错误的假设。 “文件不存在”不是
Reset(tRegister); 可能引发异常的唯一可能原因:
- 文件可能存在,但被独占锁定。
- 该文件可能存在,但您无权访问它。
- 可能存在资源错误,意味着文件存在但无法打开。
- 因此,所有异常处理代码所做的唯一事情就是引入一个错误,因为它现在能够隐藏异常的真正原因。这会使故障排除变得更加困难。
如果您想提供有关异常的更多信息,以下是更好的方法:
try
Reset(tRegister);
except
on E: Exception do
begin
//Note that the message doesn't make any assumptions about the cause of the error.
E.Message := 'Unable to open file "'+AFileName+'": ' + E.Message;
//Reraise the same exception but with extra potentially useful information.
raise;
end;
end;
第二个的问题是,即使你告诉用户这个错误,你已经对程序的其余部分隐藏了这个事实。假设您已经找到了 ProcessFile 方法的更多用途。你现在有一个例程:
- 通过电子邮件接收文件。
- 调用 ProcessFile。
- 然后删除文件和电子邮件。
如果在ProcessFile 中引发异常并且您吞下(处理)它,则上述例程将删除未处理的文件。这显然会很糟糕。如果您没有吞下异常,上述例程将跳过删除步骤,因为程序正在寻找下一个 finally/except 块。至少这样,一旦问题解决,您仍然有文件的记录,以便进行故障排除和重新处理。
第三个问题是您的异常处理程序假设您的例程将始终与用户进行交互。这限制了可重用性,因为如果您现在在服务器端应用程序中调用 ProcessFile,则会弹出一个对话框,没有人可以关闭它。
将未解决的异常留给应用程序异常处理程序处理意味着您只需要更改服务器应用程序中的默认应用程序异常处理程序,所有异常都可以记录到文件中 - 而不会弹出对话框。