0

我正在使用 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 都为真,则结果集为空:

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)。这究竟是在什么时候发生的?

4

1 回答 1

0

那是文件的代码,例如 DBF 和 CSV,而不是 SQL 远程数据集的代码。

1)RecordCount除本地文件外,不提供任何包含任何有用信息的保证。如果会 - 这意味着所有数据都从 mremote 服务器读取到本地客户端内存。调用RecordsCountSQL 意味着“我希望我的应用程序冻结一个小时,直到将所有数据库内容从服务器拉到客户端,然后因‘内存不足’错误而崩溃”。使用属性.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


关于没有得到印象,它不适用于简单的查询

您无法可靠地将简单查询与复杂查询区分开来。SELECT * FROM XXX 如果 XXX 是一个存储过程或一个 VIEW 或一个表,其中某些列是由子查询计算的虚拟数据,则 SELECT * FROM XXX 可能是非常复杂的查询。

还要重新阅读我上面写的内容。获取最终记录数意味着服务器应该将查询执行到最后。并且网络应该将所有数据传输到最后。并且TDataSet应该将所有接收到的数据缓存到内存中,以便您可以调用.Next.

想象一下对一个简单表的简单查询,该表包含 10M 行,每行 1000 字节(如客户的姓名+照片) - 总计接近 10 GB。考虑典型的 100 Mb/s(大约 10 MB/s)网络。传输所有数据以了解它们的数量需要多长时间?在大约 2Gb 传输时,您的 32 位应用程序将因内存不足错误而死。当您真正想知道是否至少有一行或没有时,所有这些都会加载。

更新 2

作为。.IsEmpty在做之前检查对while ... EOF我来说似乎有点过度设计。在这些情况下,当数据集实际上为空时,while-loop 将退出而无需进入迭代主体。因此,就个人而言,如果您没有具有用于空数据集的特定代码路径的 else 分支,则可以删除此类循环之前的此类检查。

至于缓存......这很难确定。通常有一个链条: database file -> database server + query -> db access library -> TDataSet -> Grid or other consumer

在每个箭头处,可能会或可能不会发生一些缓存。

然后是单向和双向 SQL 查询/游标。对于双向,您可以.Next.Prior任何速度前进。这对网格很有用。但是对于服务器,这意味着它要么缓存所有内部行 ID,直到游标(查询)关闭,要么引擎和索引自然允许在两个方向上进行查询。我敢打赌,数据库服务器的数据结构和算法的自然优化选择会选择前一种方法。至少我不会考虑将后者假设为可靠的隐含假设。

如果 TDataSet 也是单向的,或者如果 TDataSet 和底层库+服务器都是双向的,那么 TDataSet 将数据缓存在小块中。我估计它大约有十几行。它将创建额外的冗余网络往返或将每一行作为单独的请求获取。但获取数百或数千也将是网络不必要的负担(因此 - 延迟)。

TDbGrid 通常不缓存自身,而是将 TDataSet 的 BufferCount(未记录,参见 Joanna Carter 的源代码或文章)设置为所需的可见行数加上一些重叠以便于滚动。然而,像 QuantumGrid 这样的一些高级组件实现了它们自己的内部缓存。

因此,当您将长查询直接发送到用户将滚动到底部的某个 Grid 时,很有可能会导致延迟甚至内存耗尽。OTOH,如果您的代码是这样的while循环并且您设法将查询设置为单向(具体如何发出信号属于具体库和数据集),这将为所有链提供优化内存消耗和缓存的方法表现。当然,“提供”并不意味着所有的链条都确实实现了这一点。你肯定会失去跟.Prior,.First等的能力.Locate

这些是一般性观察,您必须做出自己的明智判断,这可能会对您的系统造成多大影响。

当您处理大量(或可能大量)数据时,没有灵丹妙药。这就是 SQL 被设计为从服务器传输尽可能少的数据的原因,在远程端进行所有过滤。更何况,WWW 潮强调分片或集群的概念,将逻辑上统一的数据库拆分到多个实际服务器中,以保持合理的内存需求。

于 2013-05-31T12:16:34.933 回答