SQLSRV 查询将输出参数作为奇怪的文本返回
SQLSRV Query Returning Output Parameter As Strange Text
我在 SQL Server 2012 中有一个存储过程,它首先检查数据是否存在,如果存在则将输出参数 @Return
设置为 1 并运行 select 查询如果不是,则将 @Return
设置为 0 并将 returns 设置为不同的 select 查询。
在测试此存储过程以确保数据准确时,它是完美的 returns 我期望的数据。问题出在 PHP 端,当它试图读取它显示 ��t_rrr
的输出参数时,它应该显示 1 或 0。我相信问题可能出在 [= 中的预定义常量中38=] 但我似乎无法让它工作。这是我的代码:
PHP:
if(isset($_GET['accno'])) {
$search = $_GET['accno'];
$return = "";
$tsql_callSP = "EXEC Intranet.CustomerSearch @Search=?, @Return=?";
$params = array(
array($search, SQLSRV_PARAM_IN),
array($return, SQLSRV_PARAM_OUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR),SQLSRV_SQLTYPE_INT),
);
/* Execute the query. */
$stmt = sqlsrv_query( $conn, $tsql_callSP, $params);
if( $stmt === false )
{
echo "EXEC Intranet.CustomerSearch Failed\n";
die( print_r( sqlsrv_errors(), true));
}
while($res = sqlsrv_next_result($stmt))
{
// make sure all result sets are stepped through,
// since the output params may not be set until this happens
}
echo $return;
if($return == 0) { ?>
//1st Result Set
while($row = sqlsrv_fetch_array($stmt)) {
}
<?php } elseif($return == 1) { // End if Return 0 ?>
//2nd Result Set
while($row = sqlsrv_fetch_array($stmt)) {
}
<?php } // End if Return 1 ?>
SQL 存储过程:
USE [Intranet]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [Intranet].[CustomerSearch]
@Search nvarchar(10)
,@Return int output
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS
(
SELECT KeyCode
FROM Autopart.dbo.Customer
WHERE KeyCode = @Search
)
BEGIN
SELECT
Customer.KeyCode
,Customer.X13
,Customer.Name
,Customer.Addra
,Customer.Addrb
,Customer.Addrc
,Customer.Addrd
,Customer.Addre
,Customer.PCode
,Customer.CTitle
,Customer.Climit
,Customer.Ptype
,Customer.Stel
,Customer.SCont
,Customer.ACont
,Customer.XString5
,Customer.Locked
,Customer.Email
,Customer.StopStatus
,CusNotes.Comment
FROM
Autopart.dbo.Customer
LEFT OUTER JOIN Autopart.dbo.CusNotes ON Autopart.dbo.Customer.KeyCode = Autopart.dbo.CusNotes.Account
WHERE
(Customer.KeyCode = @Search)
AND (CusNotes.Seqno = 1 OR CusNotes.Seqno IS NULL)
SET @Return = 1
END ELSE BEGIN
SELECT TOP 100
KeyCode
,Name
,PCode
,Addra
,Addrb
,Addrc
FROM
AUTOPART.dbo.Customer
WHERE
(KeyCode LIKE '%'+@Search+'%'
OR Name LIKE '%'+@Search+'%'
OR PCode LIKE '%'+@Search+'%')
SET @Return = 0
END
END
我尝试更改 PHP 和 SQL 类型,但无法获得所需的结果。奇怪的是,如果我创建一个 INSERT
或 UPDATE
语句的存储过程正确 OUTPUT
returns。
编辑:
SQL 服务器存储过程排序规则是 Latin1_General_CI_AS
这是由于 PHP 输出的字符编码问题。尝试使用
header ('Content-type: text/html; charset=UTF-8');
或
header ('Content-type: text/html; charset=ISO-8859-1');
此外,您可以查看 this nice article.
我正好在我的 VirtualBox 上安装了 SQL 服务器和 PHP,所以我玩了一下。
几件事:
- 使用正确类型的值初始化参数。
- 传递参数时使用
&
。
- 读取底部示例中使用
sqlsrv_next_result
. This advice is taken from sqlsrv_prepare
的存储过程 return 的所有结果。
代码如下:
$search = "qweryt";
$return = 123;
$tsql_callSP = "EXEC Intranet.CustomerSearch @Search=?, @Return=?";
$params = array(
array(&$search, SQLSRV_PARAM_IN),
array(&$return, SQLSRV_PARAM_OUT),
);
$stmt = sqlsrv_query($conn, $tsql_callSP, $params);
if ($stmt === false)
{
echo "EXEC Intranet.CustomerSearch Failed\n";
die( print_r( sqlsrv_errors(), true));
}
else
{
while($res = sqlsrv_next_result($stmt))
{
// make sure all result sets are stepped through,
// since the output params may not be set until this happens
}
}
echo $return;
已通过 PHP 5.4.28、SQL Server Express 2014、PHP SQLSRV 3.2
的 Microsoft 驱动程序进行验证
当您有一个仅执行 INSERT
或 UPDATE
的存储过程时,即不会 SELECT
的 return 结果,则 sqlsrv_next_result
会发生不需要获取 output
参数的值。如果 SELECT
连同输出参数,实际上意味着过程 return 有几个结果集,您需要读取所有这些结果集。
其实这里很similar question.
编辑
如果我没理解错的话,您想先检索 OUTPUT
参数的值。然后你想检索 SELECT
语句的结果。
据我了解,这是不可能的。 SQL 服务器 return 第一个结果集中 SELECT
语句的结果,您可以使用 sqlsrv_fetch_array
. Then you move to the second result set using sqlsrv_next_result
读取它。此时 OUTPUT
参数的值被传递给你的 PHP 变量,因为 SQL 服务器在最后一个结果集中发送 OUTPUT
参数的值。调用 sqlsrv_next_result
后,当您调用 sqlsrv_fetch_array
.
时,第一个结果集不再可用
顺便说一句,当你说
What is strange is if I create a stored procedure that is an INSERT or
UPDATE statement the OUTPUT returns correctly.
,很有道理。当存储过程没有 SELECT
语句而它有 SET NOCOUNT ON;
时,那么 returned 的唯一结果集是 OUTPUT
参数,所以你的 PHP 变量被赋予正确的值而没有显式调用 sqlsrv_next_result
.
所以,你至少有两个选择。
1) 使用sqlsrv_fetch_array
将SELECT
的完整结果读入临时数组。然后用sqlsrv_next_result
得到OUTPUT
参数的值。然后根据 OUTPUT
参数的接收值处理保存在数组中的第一个 SELECT
的结果。
2) 完全不要使用 OUTPUT
参数。有了这样的参数 SQL 服务器 return 隐含了多个结果集。由于您有多个结果集,因此 return 两个结果集明确。在你的存储过程中首先做 SELECT
那 return 只是一行和一个数字,然后你的主要 SELECT
:
ALTER PROCEDURE [Intranet].[CustomerSearch]
@Search nvarchar(10)
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS
(
SELECT KeyCode
FROM Autopart.dbo.Customer
WHERE KeyCode = @Search
)
BEGIN
SELECT CAST(1 AS int) AS ReturnCode;
SELECT
Customer.KeyCode
,Customer.X13
,Customer.Name
,Customer.Addra
,Customer.Addrb
,Customer.Addrc
,Customer.Addrd
,Customer.Addre
,Customer.PCode
,Customer.CTitle
,Customer.Climit
,Customer.Ptype
,Customer.Stel
,Customer.SCont
,Customer.ACont
,Customer.XString5
,Customer.Locked
,Customer.Email
,Customer.StopStatus
,CusNotes.Comment
FROM
Autopart.dbo.Customer
LEFT OUTER JOIN Autopart.dbo.CusNotes ON Autopart.dbo.Customer.KeyCode = Autopart.dbo.CusNotes.Account
WHERE
(Customer.KeyCode = @Search)
AND (CusNotes.Seqno = 1 OR CusNotes.Seqno IS NULL)
;
END ELSE BEGIN
SELECT CAST(0 AS int) AS ReturnCode;
SELECT TOP 100
KeyCode
,Name
,PCode
,Addra
,Addrb
,Addrc
FROM
AUTOPART.dbo.Customer
WHERE
KeyCode LIKE '%'+@Search+'%'
OR Name LIKE '%'+@Search+'%'
OR PCode LIKE '%'+@Search+'%'
;
END
END
在PHP代码中调用sqlsrv_fetch_array
第一次阅读ReturnCode
。然后调用sqlsrv_next_result
切换到第二个结果集。然后调用 sqlsrv_fetch_array
来读取主 SELECT
.
的结果
$search = "qweryt";
$tsql_callSP = "EXEC Intranet.CustomerSearch @ParamSearch=?";
$params = array
(
array(&$search, SQLSRV_PARAM_IN)
);
$stmt = sqlsrv_query($conn, $tsql_callSP, $params);
if( $stmt === false )
{
echo "EXEC Failed\n";
die( print_r( sqlsrv_errors(), true));
}
echo "First result set:\n";
while ($row = sqlsrv_fetch_array($stmt))
{
var_dump($row);
}
echo "Next result:\n";
$next_res = sqlsrv_next_result($stmt);
var_dump($next_res);
echo "Second result set:\n";
while ($row = sqlsrv_fetch_array($stmt))
{
var_dump($row);
}
我在 SQL Server 2012 中有一个存储过程,它首先检查数据是否存在,如果存在则将输出参数 @Return
设置为 1 并运行 select 查询如果不是,则将 @Return
设置为 0 并将 returns 设置为不同的 select 查询。
在测试此存储过程以确保数据准确时,它是完美的 returns 我期望的数据。问题出在 PHP 端,当它试图读取它显示 ��t_rrr
的输出参数时,它应该显示 1 或 0。我相信问题可能出在 [= 中的预定义常量中38=] 但我似乎无法让它工作。这是我的代码:
PHP:
if(isset($_GET['accno'])) {
$search = $_GET['accno'];
$return = "";
$tsql_callSP = "EXEC Intranet.CustomerSearch @Search=?, @Return=?";
$params = array(
array($search, SQLSRV_PARAM_IN),
array($return, SQLSRV_PARAM_OUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR),SQLSRV_SQLTYPE_INT),
);
/* Execute the query. */
$stmt = sqlsrv_query( $conn, $tsql_callSP, $params);
if( $stmt === false )
{
echo "EXEC Intranet.CustomerSearch Failed\n";
die( print_r( sqlsrv_errors(), true));
}
while($res = sqlsrv_next_result($stmt))
{
// make sure all result sets are stepped through,
// since the output params may not be set until this happens
}
echo $return;
if($return == 0) { ?>
//1st Result Set
while($row = sqlsrv_fetch_array($stmt)) {
}
<?php } elseif($return == 1) { // End if Return 0 ?>
//2nd Result Set
while($row = sqlsrv_fetch_array($stmt)) {
}
<?php } // End if Return 1 ?>
SQL 存储过程:
USE [Intranet]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [Intranet].[CustomerSearch]
@Search nvarchar(10)
,@Return int output
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS
(
SELECT KeyCode
FROM Autopart.dbo.Customer
WHERE KeyCode = @Search
)
BEGIN
SELECT
Customer.KeyCode
,Customer.X13
,Customer.Name
,Customer.Addra
,Customer.Addrb
,Customer.Addrc
,Customer.Addrd
,Customer.Addre
,Customer.PCode
,Customer.CTitle
,Customer.Climit
,Customer.Ptype
,Customer.Stel
,Customer.SCont
,Customer.ACont
,Customer.XString5
,Customer.Locked
,Customer.Email
,Customer.StopStatus
,CusNotes.Comment
FROM
Autopart.dbo.Customer
LEFT OUTER JOIN Autopart.dbo.CusNotes ON Autopart.dbo.Customer.KeyCode = Autopart.dbo.CusNotes.Account
WHERE
(Customer.KeyCode = @Search)
AND (CusNotes.Seqno = 1 OR CusNotes.Seqno IS NULL)
SET @Return = 1
END ELSE BEGIN
SELECT TOP 100
KeyCode
,Name
,PCode
,Addra
,Addrb
,Addrc
FROM
AUTOPART.dbo.Customer
WHERE
(KeyCode LIKE '%'+@Search+'%'
OR Name LIKE '%'+@Search+'%'
OR PCode LIKE '%'+@Search+'%')
SET @Return = 0
END
END
我尝试更改 PHP 和 SQL 类型,但无法获得所需的结果。奇怪的是,如果我创建一个 INSERT
或 UPDATE
语句的存储过程正确 OUTPUT
returns。
编辑:
SQL 服务器存储过程排序规则是 Latin1_General_CI_AS
这是由于 PHP 输出的字符编码问题。尝试使用
header ('Content-type: text/html; charset=UTF-8');
或
header ('Content-type: text/html; charset=ISO-8859-1');
此外,您可以查看 this nice article.
我正好在我的 VirtualBox 上安装了 SQL 服务器和 PHP,所以我玩了一下。
几件事:
- 使用正确类型的值初始化参数。
- 传递参数时使用
&
。 - 读取底部示例中使用
sqlsrv_next_result
. This advice is taken fromsqlsrv_prepare
的存储过程 return 的所有结果。
代码如下:
$search = "qweryt";
$return = 123;
$tsql_callSP = "EXEC Intranet.CustomerSearch @Search=?, @Return=?";
$params = array(
array(&$search, SQLSRV_PARAM_IN),
array(&$return, SQLSRV_PARAM_OUT),
);
$stmt = sqlsrv_query($conn, $tsql_callSP, $params);
if ($stmt === false)
{
echo "EXEC Intranet.CustomerSearch Failed\n";
die( print_r( sqlsrv_errors(), true));
}
else
{
while($res = sqlsrv_next_result($stmt))
{
// make sure all result sets are stepped through,
// since the output params may not be set until this happens
}
}
echo $return;
已通过 PHP 5.4.28、SQL Server Express 2014、PHP SQLSRV 3.2
的 Microsoft 驱动程序进行验证当您有一个仅执行 INSERT
或 UPDATE
的存储过程时,即不会 SELECT
的 return 结果,则 sqlsrv_next_result
会发生不需要获取 output
参数的值。如果 SELECT
连同输出参数,实际上意味着过程 return 有几个结果集,您需要读取所有这些结果集。
其实这里很similar question.
编辑
如果我没理解错的话,您想先检索 OUTPUT
参数的值。然后你想检索 SELECT
语句的结果。
据我了解,这是不可能的。 SQL 服务器 return 第一个结果集中 SELECT
语句的结果,您可以使用 sqlsrv_fetch_array
. Then you move to the second result set using sqlsrv_next_result
读取它。此时 OUTPUT
参数的值被传递给你的 PHP 变量,因为 SQL 服务器在最后一个结果集中发送 OUTPUT
参数的值。调用 sqlsrv_next_result
后,当您调用 sqlsrv_fetch_array
.
顺便说一句,当你说
What is strange is if I create a stored procedure that is an INSERT or UPDATE statement the OUTPUT returns correctly.
,很有道理。当存储过程没有 SELECT
语句而它有 SET NOCOUNT ON;
时,那么 returned 的唯一结果集是 OUTPUT
参数,所以你的 PHP 变量被赋予正确的值而没有显式调用 sqlsrv_next_result
.
所以,你至少有两个选择。
1) 使用sqlsrv_fetch_array
将SELECT
的完整结果读入临时数组。然后用sqlsrv_next_result
得到OUTPUT
参数的值。然后根据 OUTPUT
参数的接收值处理保存在数组中的第一个 SELECT
的结果。
2) 完全不要使用 OUTPUT
参数。有了这样的参数 SQL 服务器 return 隐含了多个结果集。由于您有多个结果集,因此 return 两个结果集明确。在你的存储过程中首先做 SELECT
那 return 只是一行和一个数字,然后你的主要 SELECT
:
ALTER PROCEDURE [Intranet].[CustomerSearch]
@Search nvarchar(10)
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS
(
SELECT KeyCode
FROM Autopart.dbo.Customer
WHERE KeyCode = @Search
)
BEGIN
SELECT CAST(1 AS int) AS ReturnCode;
SELECT
Customer.KeyCode
,Customer.X13
,Customer.Name
,Customer.Addra
,Customer.Addrb
,Customer.Addrc
,Customer.Addrd
,Customer.Addre
,Customer.PCode
,Customer.CTitle
,Customer.Climit
,Customer.Ptype
,Customer.Stel
,Customer.SCont
,Customer.ACont
,Customer.XString5
,Customer.Locked
,Customer.Email
,Customer.StopStatus
,CusNotes.Comment
FROM
Autopart.dbo.Customer
LEFT OUTER JOIN Autopart.dbo.CusNotes ON Autopart.dbo.Customer.KeyCode = Autopart.dbo.CusNotes.Account
WHERE
(Customer.KeyCode = @Search)
AND (CusNotes.Seqno = 1 OR CusNotes.Seqno IS NULL)
;
END ELSE BEGIN
SELECT CAST(0 AS int) AS ReturnCode;
SELECT TOP 100
KeyCode
,Name
,PCode
,Addra
,Addrb
,Addrc
FROM
AUTOPART.dbo.Customer
WHERE
KeyCode LIKE '%'+@Search+'%'
OR Name LIKE '%'+@Search+'%'
OR PCode LIKE '%'+@Search+'%'
;
END
END
在PHP代码中调用sqlsrv_fetch_array
第一次阅读ReturnCode
。然后调用sqlsrv_next_result
切换到第二个结果集。然后调用 sqlsrv_fetch_array
来读取主 SELECT
.
$search = "qweryt";
$tsql_callSP = "EXEC Intranet.CustomerSearch @ParamSearch=?";
$params = array
(
array(&$search, SQLSRV_PARAM_IN)
);
$stmt = sqlsrv_query($conn, $tsql_callSP, $params);
if( $stmt === false )
{
echo "EXEC Failed\n";
die( print_r( sqlsrv_errors(), true));
}
echo "First result set:\n";
while ($row = sqlsrv_fetch_array($stmt))
{
var_dump($row);
}
echo "Next result:\n";
$next_res = sqlsrv_next_result($stmt);
var_dump($next_res);
echo "Second result set:\n";
while ($row = sqlsrv_fetch_array($stmt))
{
var_dump($row);
}