为啥 NULL 左外连接使用 sqlsrv_fetch_array 在 PHP 中停止数据集导航?

Posted

技术标签:

【中文标题】为啥 NULL 左外连接使用 sqlsrv_fetch_array 在 PHP 中停止数据集导航?【英文标题】:Why do NULL left outer joins stop dataset navigation in PHP with sqlsrv_fetch_array?为什么 NULL 左外连接使用 sqlsrv_fetch_array 在 PHP 中停止数据集导航? 【发布时间】:2014-03-21 03:13:15 【问题描述】:

我正在将 ColdFusion 站点转换为 php,但遇到了 LEFT OUTER JOIN 和 PHP SQL 驱动程序的问题。基本上,如果你离开外连接一个不返回结果的表,然后尝试循环主表,它会在遇到数据集中的第一个空连接结果时停止。

使用

PHP 5.3.27、php_sqlsrv_53_nts_vc9.dll、SQL Server 2005

CREATE TABLE [dbo].[IS_MAIN_TABLE](
    [iMainRow] [int] NULL,
    [sMainText] [varchar](50) NULL,
    [iSubRowID] [int] NULL
)

CREATE TABLE [dbo].[IS_SUB_TABLE](
    [iSubRow] [int] NULL,
    [sSubText] [varchar](50) NULL
)

INSERT INTO IS_MAIN_TABLE(iMainRow,sMainText,iSubRowID) VALUES (1,'One',1);
INSERT INTO IS_MAIN_TABLE(iMainRow,sMainText,iSubRowID) VALUES (2,'Two',NULL);
INSERT INTO IS_MAIN_TABLE(iMainRow,sMainText,iSubRowID) VALUES (3,'Three',3);
INSERT INTO IS_MAIN_TABLE(iMainRow,sMainText,iSubRowID) VALUES (4,'Four',NULL);
INSERT INTO IS_MAIN_TABLE(iMainRow,sMainText,iSubRowID) VALUES (5,'Five',5);

INSERT INTO IS_SUB_TABLE(iSubRow,sSubText) VALUES (1,'Sub One');
INSERT INTO IS_SUB_TABLE(iSubRow,sSubText) VALUES (2,'Sub Two');
INSERT INTO IS_SUB_TABLE(iSubRow,sSubText) VALUES (3,'Sub Three');
INSERT INTO IS_SUB_TABLE(iSubRow,sSubText) VALUES (4,'Sub Four');
INSERT INTO IS_SUB_TABLE(iSubRow,sSubText) VALUES (5,'Sub Five');

现在在 Studio 中使用连接运行此选择会提供以下 5 行结果:

SELECT * FROM IS_MAIN_TABLE LEFT OUTER JOIN IS_SUB_TABLE ON IS_SUB_TABLE.iSubRow = IS_MAIN_TABLE.iSubRowID;

1   One    1    1    Sub One
2   Two    NULL NULL NULL
3   Three  3    3    Sub Three
4   Four   NULL NULL NULL
5   Five   5    5    Sub Five

然后为相同的 SQL 语句运行这个 PHP 代码:

<?php

function get_error() 
  $errors = sqlsrv_errors();
  $errtxt = "Error occured: ";
  foreach( $errors as $error) 
    $errtxt .= $error["SQLSTATE"] . ", ";
    $errtxt .= $error["code"] . ", ";
    $errtxt .= $error["message"] . "<br>";
  
  return $errtxt;


$sitedbserver = "SERVERHERE";
$sitedbuser = "userhere";
$sitedbpassword = "pwdhere";
$sitedbinstance = "INSTANCEHERE";
$sitedbconnection = array( "Database"=>$sitedbinstance, "UID"=>$sitedbuser, "PWD"=>$sitedbpassword);

$dbhandle = sqlsrv_connect($sitedbserver, $sitedbconnection) or die("Couldn't connect to SQL Server");
if(!$dbhandle) 
  echo 'Database connection failed...'; 
  exit;
 else 
  //do something


$sql = <<<EOSQL
SELECT * FROM IS_MAIN_TABLE LEFT OUTER JOIN IS_SUB_TABLE ON IS_SUB_TABLE.iSubRow = IS_MAIN_TABLE.iSubRowID;
EOSQL;

$result = sqlsrv_query($dbhandle, $sql,array(), array( "Scrollable" => SQLSRV_CURSOR_KEYSET ));
$numRows = sqlsrv_num_rows($result);

if ($numRows <> 0)    
  echo '<table id="simplegrid" cellpadding="0" cellspacing="0" >'; 
  while($row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC))
    // if($row===false) die(get_error()); 
    echo '<tr><td>' . $row['sMainText'] . '</td><td>' . $row['sSubText'] . '</td></tr>';
  
  echo '</table>'; 


?>

我只得到第一行。

One    Sub One

如果我更改 SQL 以排除 NULL...

$sql = <<<EOSQL
SELECT * FROM IS_MAIN_TABLE 
LEFT OUTER JOIN IS_SUB_TABLE ON IS_SUB_TABLE.iSubRow = IS_MAIN_TABLE.iSubRowID 
WHERE iSubRow IS NOT NULL; 
EOSQL;

我得到了三行

One    Sub One
Three  Sub Three
Five   Sub Five

在循环中取消注释 get_error 调用只会在第一行返回一个“0”,这并没有多大帮助。我希望结果实际上与在分析器/工作室中运行查询相同,但没有骰子。我遇到了连接表为空的情况,并且在处理过程中创建了一个条件,让某人知道屏幕上的数据丢失等......所以我必须找到一种方法来处理这个问题,但我现在只是难住了。我不确定将潜在的空字段合并到 '' 是否值得麻烦,或者是否有办法让空值在 PHP、驱动程序、SQL 级别默认为 ''。

【问题讨论】:

【参考方案1】:

我找到了解决办法。我不期待对所有现有查询进行重组,但似乎使用 PDO 和 PDO 驱动程序可以正确处理空连接表。使用上面相同的表/数据,但使用此代码针对 php_pdo_sqlsrv_53_nts_vc9.dll ...

try
  $conn = new PDO( "sqlsrv:server=$sitedbserver ; Database=$sitedbinstance", "$sitedbuser", "$sitedbpassword");
  $conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

catch(Exception $e)
die( print_r( $e->getMessage() ) ); 


$tsql = <<<EOSQL
SELECT * FROM IS_MAIN_TABLE 
LEFT OUTER JOIN IS_SUB_TABLE ON IS_SUB_TABLE.iSubRow = IS_MAIN_TABLE.iSubRowID; 
EOSQL;

$getProducts = $conn->prepare($tsql);
$getProducts->execute();
$products = $getProducts->fetchAll(PDO::FETCH_ASSOC);
$productCount = count($products);
if($productCount > 0)
  echo '<table id="simplegrid" cellpadding="0" cellspacing="0" >'; 
  foreach( $products as $row )
     echo '<tr><td>' . $row['sMainText'] . '</td><td>' . $row['sSubText'] . '</td></tr>';
  
  echo '</table>';   
 

我得到了预期的响应:

One     Sub One
Two 
Three   Sub Three
Four    
Five    Sub Five

我想我不应该关心为什么非 PDO 解决方案现在不起作用,因为我有一个解决方案,但是如果有人遇到这种情况并且可以阐明我很感激关闭的原因。

【讨论】:

以上是关于为啥 NULL 左外连接使用 sqlsrv_fetch_array 在 PHP 中停止数据集导航?的主要内容,如果未能解决你的问题,请参考以下文章

在 hive 中使用 Null 检查的左外连接查询不起作用

与子查询相比,为啥左外连接查询给出不同的结果?

Spark 2.2 空安全左外连接空指针异常

内连接与左外链接的区别

左外连接与右外连接区别?

linq中的左外连接