使用 FDQuery.RecordCount 判断查询是否为空可能 return Delphi 中的负值
Using FDQuery.RecordCount to tell if the query is empty or not may return negative value in Delphi
我在表单上有一个 FDQuery,还有一个根据其 recordCount(>0 时启用)启用和禁用组件的操作。
大多数时候 recordCount 属性 return 查询中的实际记录数。有时,recordcount return 为负值,但我可以在与查询关联的网格上看到记录。
RecordCount returned 值介于 -5 和 -1 之间,直到现在。
我该如何解决这个问题?为什么它 return 负值?
why does it return negative values?
这不是 FireDAC 特有的,这是所有提供 TDataSet
接口的 Delphi 库的正常行为。
TDataSet.RecordCount
从未被保证工作。它甚至在文档中说。这是一个奖励功能。有时它可能会起作用,有时则不会。预计它只能可靠地用于非 SQL table 数据,如 CSV、DBF、Paradox tables 和其他类似的 ISAM 引擎。
How can I solve this?
在现代世界中依赖此功能始终是一项冒险的尝试。
所以你最好设计你的程序,让这个功能永远不会被使用,或者只在极少数非常特定的场景中使用。
相反,您应该了解您的程序真正向库提出了什么问题,然后找到一个 "language" 通过调用其他函数来提出这个问题,更适合您的情况。
现在告诉我,当您在 Google 中搜索时,您阅读第 10 页、第 100 页的频率如何?几乎从来没有,对吧?您的程序用户也几乎永远不会向下滚动数据网格。请记住这一点。
您始终需要首先向用户展示数据并快速完成。但很少有最后的数据。
现在,三个例子。
1) 您从某个网速较慢的远程服务器读取数据。您每秒只能读取 100 行。您的网格有空间显示前 20 行。其余用户必须滚动浏览。查询总共可以为您筛选 10 000 行。
如果您只向用户显示这 20 行 - 那么它几乎可以立即运行,从您开始读取数据到填充网格并将其呈现给用户仅需 0.2 秒。其余数据只有在用户通过滚动请求时才会被获取(为了清楚起见,我在这里做了一些简化,我知道预缓存 TDataset.Buffers
)。
那么,如果您调用 RecordCount
函数,您的程序会做什么?它将所有记录下载到您的本地内存中并对其进行计数。以这样的速度需要 10 000 / 100 = 100 秒,超过一分半钟。
只需调用 RecordCount
函数,您就调用了 FetchAll
过程,并使您的程序响应用户的时间不是 0.2 秒,而是 1 分 40 秒。
用户会非常紧张地等待它完成。
2) 假设您正在从某个存储过程中获取数据。或者来自 table,另一个应用程序正在其中插入行。换句话说,那不是一些静态的只读数据,而是在您下载它时正在生成的实时数据。
那么,有多少行呢?比如说现在是1000行,一秒后是1010行,两秒后可能是1050行,以此类推。
当此值不时更改时,唯一真值是什么?
太棒了,你调用了 RecordCount
函数,你 SetLength
将数组变成了 1000,现在你从查询中读取了所有数据。但是下载数据需要一些时间。它通常很快,但绝不会是瞬时的。因此,例如,您花了一秒钟的时间将这 1000 行数据库查询数据下载到您的数组(或网格)中。但是,当您执行此操作时,还有 10 行 generated/inserted,并且您的查询未被 .EOF
ed,并且您继续获取行 #1001、#1002、... #1010 - 然后将它们放入array/gris 行根本不存在!
好不好?
或者您会在超出 array/grid 边界时取消查询吗?
这样你就不会遇到访问冲突。
但是相反,您会忽略和错过最近的 10 行。
这样好吗?
3) 您的查询,当您调试它时,returns 10 000 行。你通过调用 RecordCount
函数将它们全部下载到你程序的本地内存中,它就像一个魅力,你部署它。
您的客户使用您的程序,数据不断增长,有一天您的查询 returns 不是 10 000 行,而是 10 000 000 行。
您的程序调用 RecordCount
函数来下载所有这些行,例如,它下载了 9 000 000 百万...
....然后它因 Out Of Memory
错误而崩溃。
enable and disable components according to its recordCount > 0
获取您永远不需要的数据(确切的行数)然后丢弃它的方法是错误的。上面的示例向您展示了这如何使您的程序变得脆弱和缓慢。
您真正想知道的是是否有任何行或 none。
您不需要计算所有行的数量并了解它们的数量,您只需要知道查询是否为空。
这正是您应该通过调用 TDataSet.IsEmpty
函数而不是 RecordCount
来询问的问题。
我在表单上有一个 FDQuery,还有一个根据其 recordCount(>0 时启用)启用和禁用组件的操作。
大多数时候 recordCount 属性 return 查询中的实际记录数。有时,recordcount return 为负值,但我可以在与查询关联的网格上看到记录。
RecordCount returned 值介于 -5 和 -1 之间,直到现在。
我该如何解决这个问题?为什么它 return 负值?
why does it return negative values?
这不是 FireDAC 特有的,这是所有提供 TDataSet
接口的 Delphi 库的正常行为。
TDataSet.RecordCount
从未被保证工作。它甚至在文档中说。这是一个奖励功能。有时它可能会起作用,有时则不会。预计它只能可靠地用于非 SQL table 数据,如 CSV、DBF、Paradox tables 和其他类似的 ISAM 引擎。
How can I solve this?
在现代世界中依赖此功能始终是一项冒险的尝试。 所以你最好设计你的程序,让这个功能永远不会被使用,或者只在极少数非常特定的场景中使用。 相反,您应该了解您的程序真正向库提出了什么问题,然后找到一个 "language" 通过调用其他函数来提出这个问题,更适合您的情况。
现在告诉我,当您在 Google 中搜索时,您阅读第 10 页、第 100 页的频率如何?几乎从来没有,对吧?您的程序用户也几乎永远不会向下滚动数据网格。请记住这一点。 您始终需要首先向用户展示数据并快速完成。但很少有最后的数据。
现在,三个例子。
1) 您从某个网速较慢的远程服务器读取数据。您每秒只能读取 100 行。您的网格有空间显示前 20 行。其余用户必须滚动浏览。查询总共可以为您筛选 10 000 行。
如果您只向用户显示这 20 行 - 那么它几乎可以立即运行,从您开始读取数据到填充网格并将其呈现给用户仅需 0.2 秒。其余数据只有在用户通过滚动请求时才会被获取(为了清楚起见,我在这里做了一些简化,我知道预缓存 TDataset.Buffers
)。
那么,如果您调用 RecordCount
函数,您的程序会做什么?它将所有记录下载到您的本地内存中并对其进行计数。以这样的速度需要 10 000 / 100 = 100 秒,超过一分半钟。
只需调用 RecordCount
函数,您就调用了 FetchAll
过程,并使您的程序响应用户的时间不是 0.2 秒,而是 1 分 40 秒。
用户会非常紧张地等待它完成。
2) 假设您正在从某个存储过程中获取数据。或者来自 table,另一个应用程序正在其中插入行。换句话说,那不是一些静态的只读数据,而是在您下载它时正在生成的实时数据。
那么,有多少行呢?比如说现在是1000行,一秒后是1010行,两秒后可能是1050行,以此类推。
当此值不时更改时,唯一真值是什么?
太棒了,你调用了 RecordCount
函数,你 SetLength
将数组变成了 1000,现在你从查询中读取了所有数据。但是下载数据需要一些时间。它通常很快,但绝不会是瞬时的。因此,例如,您花了一秒钟的时间将这 1000 行数据库查询数据下载到您的数组(或网格)中。但是,当您执行此操作时,还有 10 行 generated/inserted,并且您的查询未被 .EOF
ed,并且您继续获取行 #1001、#1002、... #1010 - 然后将它们放入array/gris 行根本不存在!
好不好?
或者您会在超出 array/grid 边界时取消查询吗?
这样你就不会遇到访问冲突。
但是相反,您会忽略和错过最近的 10 行。
这样好吗?
3) 您的查询,当您调试它时,returns 10 000 行。你通过调用 RecordCount
函数将它们全部下载到你程序的本地内存中,它就像一个魅力,你部署它。
您的客户使用您的程序,数据不断增长,有一天您的查询 returns 不是 10 000 行,而是 10 000 000 行。
您的程序调用 RecordCount
函数来下载所有这些行,例如,它下载了 9 000 000 百万...
....然后它因 Out Of Memory
错误而崩溃。
enable and disable components according to its recordCount > 0
获取您永远不需要的数据(确切的行数)然后丢弃它的方法是错误的。上面的示例向您展示了这如何使您的程序变得脆弱和缓慢。
您真正想知道的是是否有任何行或 none。 您不需要计算所有行的数量并了解它们的数量,您只需要知道查询是否为空。
这正是您应该通过调用 TDataSet.IsEmpty
函数而不是 RecordCount
来询问的问题。