我正在使用TSQLConnection和TSQLDataSet从Delphi应用程序查询SQL Server(2012)数据库。到目前为止我的所有查询都运行正常,但我现在正在尝试使用INNER JOIN编写SELECT查询,并且我无法访问TSQLDataSet的任何输出。
代码:
Query_text:='SELECT Table1.Price
'FROM [Table1]
'INNER JOIN [Table2]
'ON Table1.Code_ID = Table2.ID'
'WHERE (Table2.Code = '+QuotedStr(Temp_code)+')';
SQL_dataset.CommandType:=ctQuery;
SQL_dataset.CommandText:=Query_text;
SQL_dataset.Open;
If SQL_dataset.RecordCount>0 then .... { THIS RETURNS NOTHING }
如果我将此查询输入SSMS,则返回正确的信息。在我使用的所有其他SELECT查询(没有INNER JOIN)中,SQL_dataset按预期返回记录计数和字段名。
关于问题是什么以及如何解决它的任何想法?
更新:
我在TSQLDataset.RecordCount上的信息:
http://docwiki.embarcadero.com/Libraries/XE4/en/Data.SqlExpr.TCustomSQLDataSet.RecordCount
由此我没有得到一个简单的查询无法使用的印象 - 到目前为止,我已成功使用它简单的SELECT查询作为查询是否返回任何数据的标志...我只是幸运?但是,上面的链接指出它不能用于参数化查询和多表连接,所以这似乎解释了我原来的问题!非常感谢你指出我正确的方向。
此链接表明如果Bof和Eof都为true,则结果集为空:
http://docwiki.embarcadero.com/Libraries/XE4/en/Data.DB.TDataSet.Eof
If SQL_dataset.Bof=True and SQL_dataset.Eof=True then
begin
Found:=False;
这是一个更好的选择吗?
更新2:
感谢您的解释,这对我来说已经开始变得有意义了。我删除了对RecordCount的所有引用,并按照建议替换为TSQLDataset.isEmpty(我完全错过了该方法,谢谢)。
我原以为只要你调用TSQLDataset.Open就会填充TSQLDataset.RecordCount,但是如果我理解正确的话不是这样的话吗?
有时我会按如下方式滚动搜索结果:
SQL_dataset.CommandType:=ctQuery;
SQL_dataset.CommandText:=Query_text;
SQL_dataset.Open;
If SQL_dataset.IsEmpty=False then
begin
SQL_dataset.First;
While not SQL_dataset.Eof do
begin
{ DO SOMETHING }
SQL_dataset.Next;
end;
end;
这显然会调用TSQLDataset.Next,所以我假设这会完成你所谈论的所有内存缓冲(根据RecordCount)。这到底发生了什么?
答案 0 :(得分:0)
这是文件的代码,如DBF和CSV,而不是SQL远程数据集。
1)不保证RecordCount
包含除本地文件之外的任何有用信息。如果它 - 它意味着所有数据都从mremote服务器读取到本地客户端内存。为SQL调用RecordsCount
意味着"我希望我的应用程序冻结一小时,直到所有数据库内容都从服务器提取到客户端,然后因内存不足而崩溃。错误&#34 ;.使用属性.Empty
,.BOF
和.EOF
实际上,你从哪里得到RecordCount
???当您阅读文档时,您确实看到文档明确指出RecordCount
与数据库中的记录数不对应,因此检查RecordCount
> 0对数据库数据一无所知。
http://docwiki.embarcadero.com/Libraries/XE4/en/Data.DB.TDataSet.RecordCount
2)使用参数。
试试这样:
with SQL_dataset do begin
Close;
CommandType := ctQuery;
ParamCheck := true;
CommandText := 'SELECT Table1.Price FROM "Table1" ' +
'INNER JOIN "Table2" ON Table1.Code_ID = Table2.ID ' +
'WHERE Table2.Code = :Temp_code ';
Params[0].AsString := 'abcdefgh';
Open;
if not IsEmpty then begin
....
end;
end;
另外,请编辑问题的标签并指定Delphi版本并指定数据库访问驱动程序
<强>更新强>
关于
if SQL_dataset.Bof=True and SQL_dataset.Eof=True then
begin
Found:=False;
这可以写得更简单。
Found := not (SQL_dataset.Bof and SQL_dataset.Eof)
或现代德尔福
Found := not SQL_dataset.IsEmpty;
http://docwiki.embarcadero.com/Libraries/XE4/en/Data.DB.TDataSet.IsEmpty
关于没有得到印象,它不适用于简单的查询
您无法可靠地告诉复杂的查询。如果XXX是存储过程或VIEW或表,其中某些列是通过子查询计算的虚拟数据,则SELECT * FROM XXX可能是非常复杂的查询。
同时重读我上面写的内容。获取最终记录数意味着服务器应该执行查询到最后。并且网络应该将所有数据传输到最后。并且TDataSet
应该将所有收到的数据缓存到内存中,以便您可以调用.Next
。
想象一下对一个简单表的简单查询,该表包含10M行,每行1000个字节(如名称+客户端照片) - 总共接近10 GB。考虑典型的100 Mb / s(约10 MB / s)网络。转移所有数据只是为了了解他们的数量需要多长时间?在大约2Gb转移你的32位应用程序将死于内存不足的错误。 当你真正想知道是否至少有一行或没有一行时,所有这些都会加载。
更新2
AS。在.IsEmpty
之前检查while ... EOF
似乎对我来说有点过度工程。在这些情况下,当数据集实际上是空的时,循环将退出w / o无论如何进入迭代体。所以,就个人而言,如果你没有带有空数据集特定代码路径的else-branch,那么可以删除这样的循环之前的检查。
至于缓存......这很难确定。通常有一个链:
database file -> database server + query -> db access library -> TDataSet -> Grid or other consumer
在每个箭头处,某些缓存可能会也可能不会发生。
然后是单向和双向SQL查询/游标。
对于双向,您可以无论如何都可以.Next
和.Prior
。这对网格很有用。但是对于服务器而言,这意味着它要么缓存所有内部行ID,直到光标(查询)关闭,或者引擎和索引自然允许在两个方向上继续查询。我敢打赌,DB服务器的数据结构和算法的自然优化选择选择了以前的方法。至少我不会考虑将后者视为可靠的隐含假设。
如果TDataSet也是单向的,或者如果TDataSet和底层库+服务器都是双向的,那么TDataSet会以小块的形式缓存数据。我估计它有十几行。它将创建额外的冗余网络往返,将每行作为单独的请求获取。但是,在网络(因此 - 延迟)上获取数百或数千也是不必要的负担。
TDbGrid通常不会自我缓存,而是将TDataSet的BufferCount(未记录,请参阅Joanna Carter的资料或文章)设置为所需的可见行数加上一些重叠以便于滚动。但是,像QuantumGrid这样的一些高级组件会实现自己的内部缓存。
因此,当您将长查询直接放入某个Grid时,用户会滚动到底部,很可能会导致延迟甚至内存耗尽。
OTOH,如果您的代码就像while循环,并且您设法将查询设置为单向(具体如何发信号通知这属于具体的库和数据集),这将为所有链提供优化内存消耗和缓存的方法性能。当然&#34;提供&#34;并不意味着所有的链实际上都实现了这一点。当然,您无法拨打.Prior
,.First
,.Locate
等电话。
这些是一般性观察,您必须自己明智地判断这可能对您的系统造成多大影响。
当您处理大量(或可能很大)数据时,没有灵丹妙药。这就是为什么SQL被设计为尽可能少地从服务器传输数据,在远程端进行所有过滤。更重要的是,当逻辑上统一的数据库被分成几个实际的服务器时,WWW潮流强调了分片或聚类的概念,以保持内存需求的合理性。