我在远程计算机上有一个 PHP 脚本,它从 Microsoft SQL Server 数据库中提取数据并将数据插入到 MySQL 数据库中。我设置了一个 cron 作业来每小时运行该脚本,以便 MySQL 数据库与 SQL Server 数据库保持同步。
我的问题是,每当我在远程计算机上运行脚本时,对 SQL Server 的查询结果都是不完整的,并且在给定相同参数时会有所不同。无论我从命令行还是使用 cronjob 运行它,它都不会检索正确的行数。
但是,如果我在不同的环境(在 SQL Server Management Studio 中或本地测试脚本中)运行查询,结果始终是正确的。
我的第一个想法是这是一个环境配置问题,但我发现使用
init_set()
函数将 max_execution_time
设置为 0,max_input_time
设置为 0,memory_limit
设置为 -1(根据 php.ini 文件)。 ini 指令文档)对查询结果没有影响。
我的第二个想法是,这可能是远程计算机上的防火墙或网络设置的问题,但计算机上没有直接安装防火墙,并且虽然网络设置对入站连接极为严格,但出站连接却完全开放。 我在网上看到了一些类似的问题 -
(1)、(2)、(3)、(4) - 但由于具体情况,解决方案要么不起作用,要么不适用我正在运行的查询与他们正在运行的查询的比较。
我不确定还有什么问题。本地测试脚本如下:
<?php
$connection_info = array(
"UID"=>"UserID",
"PWD"=>"password",
"Database"=>"database_name"
);
$serverName = 'database_host';
try{
$conn = sqlsrv_connect($serverName, $connection_info);
} catch(Exception $e){
die("Not able to connect.");
}
echo "Connected!\n";
$sql = "
SELECT
SP.SaleInvoiceID,
SP.AssociationNumber,
SP.Priority,
S.Abbreviation,
GP.GlobalProductID,
SI.OrderEntryID,
SI.OriginalSaleInvoiceID,
SI.AccountsReceivableID,
SI.EmployeeID3,
SI.InvoiceIDByStore AS [invoice],
(C.Customer_Name + ' ' + C.First_Name + ' ' + C.Last_Name) AS [customer],
C.Id_Number as [customer_id],
GP.ProductIdentifier AS [product_sku],
RGP.ManufacturerPartNumber AS [model],
CAST( L2.FieldText AS varchar ) AS [manufacturer],
CAST( L1.FieldText AS varchar ) AS [description],
CAST( L3.FieldText AS varchar ) AS [category],
SP.Quantity AS Qty,
SP.SerialNumber,
SP.ContractNumber,
SP.MobileNumber,
GP.CategoryNumber,
SP.UnitPrice,
SP.UnitCost,
SP.ListPrice,
SI.SplitRate1,
SI.SplitRate2,
SI.EmployeeID1,
SI.EmployeeID2,
E1.Employee_Name AS [Employee1],
E2.Employee_Name AS [Employee2],
SI.DateCreated,
SI.Comments
FROM [database_name].[dbo].[SaleInvoices] SI
INNER JOIN [database_name].[dbo].[SaleInvoicesAndProducts] SP
ON SI.SaleInvoiceID = SP.SaleInvoiceID
INNER JOIN [database_name].[dbo].[iQclerk_GlobalProducts] GP
ON SP.GlobalProductID = GP.GlobalProductID
INNER JOIN [database_name].[dbo].[LanguageTranslations] L1
ON GP.ProductNameID = L1.ReferenceID
INNER JOIN [database_name].[dbo].[Stores] S
ON S.StoreID = SI.StoreID1
INNER JOIN [database_name].[dbo].[Customer_Information] C
ON SI.CustomerID1 = C.Id_Number
INNER JOIN [database_name].[dbo].[Employees] E1
ON E1.Id_Number = SI.EmployeeID1
INNER JOIN [database_name].[dbo].[Categories] CAT
ON CAT.CategoryNumber = GP.CategoryNumber
INNER JOIN [database_name].[dbo].[LanguageTranslations] L3
ON CAT.CategoryPathID = L3.ReferenceID
LEFT OUTER JOIN [database_name].[dbo].[Employees] E2
ON E2.Id_Number = SI.EmployeeID2
LEFT OUTER JOIN [database_name].[dbo].[RegularGlobalProducts] RGP
ON RGP.GlobalProductID = GP.GlobalProductID
LEFT OUTER JOIN [database_name].[dbo].[Manufacturers] M
ON M.ManufacturerID = RGP.ManufacturerID
LEFT OUTER JOIN [database_name].[dbo].[LanguageTranslations] L2
ON M.ManufacturerNameID = L2.ReferenceID
WHERE SI.DateCreated
BETWEEN cast( '2024-03-04 00:00:00' AS DateTime )
AND cast( '2024-05-09 23:59:59' AS DateTime )
AND L3.LanguageCode = 'en-us'
";
$result = sqlsrv_query($conn, $sql, array(), array("Scrollable" => 'static'));
print("Data package received, etrieving data from package...\n");
$result_rows = array();
$i = 1;
while( $row = sqlsrv_fetch_array( $result, SQLSRV_FETCH_ASSOC ) ){
print("Records retrieved: ".$i."\r");
$i++;
array_push($result_rows, $row);
}
print("\n");
print(sizeof($result_rows));
数据流在远程机器上更加分离:
<?php
class SQLSRV{
protected $dbConn;
public function open_db($server, $user, $password, $database, $throwError=false)
{
global $_REGISTRY;
ini_set("max_execution_time","0");
ini_set("max_input_time","0");
ini_set("memory_limit","-1" );
$errorMsg = "SQLSRV_ERROR: Cannot connect to SQL server database!\n";
$serverName = $server;
$connectionInfo = array(
'UID' => $user,
'PWD' => $password,
'DATABASE' => $database
);
$this->dbConn = sqlsrv_connect($serverName, $connectionInfo);
if (!$this->dbConn) {
$_REGISTRY['STATE'] = 'FATAL';
$_REGISTRY['ERROR_MESSAGE'] = 'DATABASE_CONNNECTION_ERROR';
if($throwError) throw new \ErrorException( $errorMsg, 10001, 0,__file__, __line__ );
else return $errorMsg;
}
return true;
}
public function query( $sql, $throwError=true )
{
if( !$sql ) return null;
if(!$throwError) {
sqlsrv_configure("WarningReturnAsErrors", 0);
}
$errorMsg = "SQLSRV_ERROR: No database connection";
if(!$this->dbConn )
{
if( $throwError) throw new \ErrorException( $errorMsg, 10001, 0,__file__, __line__ );
else return $errorMsg;
}
$query = false;
try {
$query = sqlsrv_query( $this->dbConn, $sql, array(), array("Scrollable" => 'static') );
} catch( Exception $e ) {}
$queryError = 'SQLSRV_ERROR: ' . sqlsrv_errors() . "\n\n<pre>$sql</pre>\n";
if(!$throwError) {
sqlsrv_configure("WarningReturnAsErrors", 1);
}
if (!$query) {
if($throwError) throw new \Exception( $queryError );
else return $queryError;
} else {
return $query;
}
}
public function fetchData( $sql, $throwError=true )
{
$res = $this->query( $sql, $throwError );
if($res) {
$queryError = 'SQLSRV_ERROR: ' . sqlsrv_errors() . "\n\n$sql\n";
file_put_contents('/var/tmp/sqlsrv.log', "fetchData Res: $queryError\n", FILE_APPEND );
try{
if(sqlsrv_num_rows($res)) {
$ar = array();
while( $row = sqlsrv_fetch_array( $res, SQLSRV_FETCH_ASSOC ) )
{
array_push($ar, $row);
}
return $ar;
}
} catch( \Exception $e ) { return false;}
} else return false;
}
}
$sql_srv_obj = new SQLSRV();
$sql_srv_obj->open_db('database_host', 'username', 'password', 'database_name');
$sql_query = "
SELECT
SP.SaleInvoiceID,
SP.AssociationNumber,
SP.Priority,
S.Abbreviation,
GP.GlobalProductID,
SI.OrderEntryID,
SI.OriginalSaleInvoiceID,
SI.AccountsReceivableID,
SI.EmployeeID3,
SI.InvoiceIDByStore AS [invoice],
(C.Customer_Name + ' ' + C.First_Name + ' ' + C.Last_Name) AS [customer],
C.Id_Number as [customer_id],
GP.ProductIdentifier AS [product_sku],
RGP.ManufacturerPartNumber AS [model],
CAST( L2.FieldText AS varchar ) AS [manufacturer],
CAST( L1.FieldText AS varchar ) AS [description],
CAST( L3.FieldText AS varchar ) AS [category],
SP.Quantity AS Qty,
SP.SerialNumber,
SP.ContractNumber,
SP.MobileNumber,
GP.CategoryNumber,
SP.UnitPrice,
SP.UnitCost,
SP.ListPrice,
SI.SplitRate1,
SI.SplitRate2,
SI.EmployeeID1,
SI.EmployeeID2,
E1.Employee_Name AS [Employee1],
E2.Employee_Name AS [Employee2],
SI.DateCreated,
SI.Comments
FROM [database_name].[dbo].[SaleInvoices] SI
INNER JOIN [database_name].[dbo].[SaleInvoicesAndProducts] SP
ON SI.SaleInvoiceID = SP.SaleInvoiceID
INNER JOIN [database_name].[dbo].[iQclerk_GlobalProducts] GP
ON SP.GlobalProductID = GP.GlobalProductID
INNER JOIN [database_name].[dbo].[LanguageTranslations] L1
ON GP.ProductNameID = L1.ReferenceID
INNER JOIN [database_name].[dbo].[Stores] S
ON S.StoreID = SI.StoreID1
INNER JOIN [database_name].[dbo].[Customer_Information] C
ON SI.CustomerID1 = C.Id_Number
INNER JOIN [database_name].[dbo].[Employees] E1
ON E1.Id_Number = SI.EmployeeID1
INNER JOIN [database_name].[dbo].[Categories] CAT
ON CAT.CategoryNumber = GP.CategoryNumber
INNER JOIN [database_name].[dbo].[LanguageTranslations] L3
ON CAT.CategoryPathID = L3.ReferenceID
LEFT OUTER JOIN [database_name].[dbo].[Employees] E2
ON E2.Id_Number = SI.EmployeeID2
LEFT OUTER JOIN [database_name].[dbo].[RegularGlobalProducts] RGP
ON RGP.GlobalProductID = GP.GlobalProductID
LEFT OUTER JOIN [database_name].[dbo].[Manufacturers] M
ON M.ManufacturerID = RGP.ManufacturerID
LEFT OUTER JOIN [database_name].[dbo].[LanguageTranslations] L2
ON M.ManufacturerNameID = L2.ReferenceID
WHERE SI.DateCreated
BETWEEN cast( '2024-03-04 00:00:00' AS DateTime )
AND cast( '2024-05-09 23:59:59' AS DateTime )
AND L3.LanguageCode = 'en-us'
";
$data = $sql_srv_obj->fetchData($sql_query);
所有查询方式的登录信息都是相同的。
//Old format
$result = sqlsrv_query($conn, $sql, array(), array("Scrollable" => 'static'));
//New new format
$result = sqlsrv_query($conn, $sql, array(), array("Scrollable" => SQLSRV_CURSOR_STATIC));
虽然旧格式在我的本地计算机上有效,但它不是传递到 sqlsrv_query()
函数的有效值。 SQLSRV_CURSOR_STATIC 是在 SQLSRV 包中定义的常量。