PHP PDO ODBC 意外的空结果集

Posted

技术标签:

【中文标题】PHP PDO ODBC 意外的空结果集【英文标题】:PHP PDO ODBC unexpected empty result set 【发布时间】:2015-10-01 02:49:24 【问题描述】:

我正在尝试通过与 SQL Server 数据库的 ODBC 连接来查找使用 PDO 的问题,在该数据库中,我得到了一个已知良好查询的空结果集。我将不胜感激来自社区的任何指导。这是我已经工作了大约五年的大型系统的一部分;它采用报告的 XML 表示,从中生成 SQL,运行查询,根据请求格式化结果集,并生成用于演示的网页。比您可能需要知道的要多,但我试图传达的是,我非常了解它应该如何工作,并且在大多数情况下它可以可靠地工作。但是我有一个客户想要一些新的东西,它破坏了我的系统。

我将此称为已知良好查询,因为我可以将查询从我的日志文件复制并粘贴到 SSMS(SQL Server 控制台)并运行它。它产生 62 行结果。但是当我通过 PDO 运行相同的查询时,我得到一个 PDOStatement,没有errorInfo(),没有抛出异常,等等。但是fetchAll() 返回一个空数组。我最初使用query(),但如果查询中缺少某些内容,使用prepare()execute() 似乎更安全。没什么区别。

我意识到可能存在类型转换问题,但在下面的示例中,检索到的两个字段分别为 nvarchar(128) 和 nvarchar(32) 类型,它们与其他查询一起成功返回。

我应该提到,查询在应用程序中只执行一次,所以据我所知,这不是先前执行干扰下一个执行的问题。另外,PDO 对象有 setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

这是execute()返回的PDOStatement:

Result Set PDOStatement Object
(
    [queryString] => SELECT [dbo].[Supplier].[SupplierName] AS suppliername,[dbo].[Item].[ItemLookupCode] AS itemlookupcode FROM [dbo].[Order] LEFT JOIN [dbo].[OrderEntry] ON [dbo].[Order].ID=[dbo].[OrderEntry].OrderID LEFT JOIN [dbo].[Item] ON [dbo].[Item].ID=[dbo].[OrderEntry].ItemID,[dbo].[Supplier] WHERE ([dbo].[Order].Time >= '2015-01-01 00:00:00') AND ([dbo].[Order].Time <= '2015-03-31 23:59:59') AND ([dbo].[Item].SupplierID=[dbo].[Supplier].ID) ORDER BY [dbo].[Supplier].[SupplierName]
)

这并不复杂,其他 SQL 查询对该数据库也能正常工作。这只是通过 PDO 失败,但在 SSMS 中工作。

有什么想法吗?有没有人见过这种行为?有没有其他方法可以查看这里发生了什么?我已经查看了关于这个主题的几个问题,但它们似乎都有一些我没有做的错误。

顺便说一句,php 5.4.22。

【问题讨论】:

【参考方案1】:

在将您的查询分解为我在下面使用的格式后,我注意到您混合了显式连接(LEFT JOIN、INNER JOIN 等)和隐式连接(FROM table1、table2)。这不仅被认为是一种非常糟糕的做法,而且已知有时会导致异常和意外的查询响应。因此,查看连接的隐含逻辑,我将查询重写如下:

SELECT 
    [dbo].[Supplier].[SupplierName] AS suppliername,
    [dbo].[Item].[ItemLookupCode] AS itemlookupcode 
FROM [dbo].[Order] 
INNER JOIN [dbo].[OrderEntry] 
    ON [dbo].[Order].ID=[dbo].[OrderEntry].OrderID 
INNER JOIN [dbo].[Item] 
    ON [dbo].[Item].ID=[dbo].[OrderEntry].ItemID
INNER JOIN [dbo].[Supplier]
    ON [dbo].[Item].SupplierID=[dbo].[Supplier].ID
WHERE ([dbo].[Order].Time >= '2015-01-01 00:00:00') 
    AND ([dbo].[Order].Time <= '2015-03-31 23:59:59') 
ORDER BY [dbo].[Supplier].[SupplierName]

我将您查询中的 LEFT JOIN 更改为 INNER JOIN,因为 [Item].SupplierID 和 [Supplier].ID 必须在您的原始查询中匹配(因此存在,因为 equals 不会返回 TRUE 值如果其中一个或两个值为 NULL。)因此,OrderEntry 值也必须存在才能返回有效响应。如果该行必须存在才能返回有效数据,则应始终使用 INNER JOIN - 它简化了内部逻辑,并且通常可以加快查询响应。

我意识到在这一点上这是一个老问题,但一个好的答案永远不会浪费。

【讨论】:

【参考方案2】:

我不同意你的观点。如果我手工编写 SQL 查询,它们会像你的一样出来。

但是这里的上下文是不同的。在这个系统 (http://www.calast.com/DynaCRUX.html) 中,有一个以 XML 表示的数据库表及其关系的抽象。而且,还有所需报告的抽象,也是 XML 格式的。创建 SQL 的应用程序必须处理给定的输入。有时有足够的信息来生成像你我一样的“好”SQL。有时没有,让系统尽其所能。回退有时是 ANSI 之前的连接语法。

另外,我确实指出(我们可能认为它很丑),生成的查询 (1) 是合法的 T-SQL,并且 (2) 在 SSMS 中运行时产生了客户想要的输出。事实证明,问题从来不在查询中。这只是我系统中的一个配置错误,所以我需要关闭这个问题。

也就是说,我最近重写了 SQL 生成引擎,以使用一种不同的方法来生成更合理的查询。也就是说,那些看起来像你我会写的。

你的回答很好,我怀疑它会帮助其他人写出更好的查询。

【讨论】:

我知道这是一篇相当老的帖子@calast,但我面临的问题与您描述的完全相同。您为什么不在系统中包含“配置错误”,以防任何发现您的问题相关的人受益?

以上是关于PHP PDO ODBC 意外的空结果集的主要内容,如果未能解决你的问题,请参考以下文章

PHP使用PDOStatement处理结果集

PHP中的PDO操作学习查询结构集

PHP中的PDO操作学习查询结构集

PDO中获取结果集

php pdo - 检索结果和行数 - 便携式解决方案

PDO中获取结果集之fetchColumn()方法详解