【问题标题】:SQLSRV Query Returning Output Parameter As Strange TextSQLSRV 查询将输出参数返回为奇怪的文本
【发布时间】:2015-07-29 19:01:13
【问题描述】:

我在 SQL Server 2012 中有一个存储过程,它首先检查数据是否存在,如果存在,则将输出参数 @Return 设置为 1 并运行选择查询,如果不存在,则将 @Return 设置为 0 并返回一个不同的选择查询。

在测试此存储过程以确保数据准确时,它是完美的并返回我期望的数据。问题出在 PHP 端,当它试图读取它显示的输出参数时,它应该显示 1 或 0 时显示 ��t_rrr。我相信问题可能出在 sqlsrv_query 中的预定义常量中,但我似乎无法得到它在职的。这是我的代码:

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 类型,但无法获得所需的结果。奇怪的是,如果我创建一个存储过程,它是 INSERTUPDATE 语句,OUTPUT 会正确返回。

编辑:

SQL Server 存储过程排序规则是 Latin1_General_CI_AS

【问题讨论】:

  • 尝试:$return = 0; 代替 $return = "";SQLSRV_PARAM_INOUT 代替 SQLSRV_PARAM_OUTSQLSRV_PHPTYPE_INT(SQLSRV_ENC_BINARY) 代替 SQLSRV_PHPTYPE_STRING
  • 按照建议更改$return 变量,将参数类型更改为SQLRV_PARAM_INOUT,将PHPTYPE 更改为array($return, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_INT,SQLSRV_SQLTYPE_INT), 现在无论查询如何,我总是返回0(虽然没有愚蠢的字符)和@ 987654338@ 检测为 ASCII

标签: php sql sql-server stored-procedures sqlsrv


【解决方案1】:

我碰巧在我的 VirtualBox 上安装了 SQL Server 和 PHP,所以我玩了一下。

几件事:

  • 使用正确类型的值初始化参数。
  • 传递参数时使用&amp;
  • 使用sqlsrv_next_result 读取存储过程返回的所有结果。此建议取自底部示例中的sqlsrv_prepare

代码如下:

$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、Microsoft 驱动程序 PHP SQLSRV 3.2 验证

如果您有一个只执行INSERTUPDATE 的存储过程,即不返回SELECT 的结果,那么恰好不需要sqlsrv_next_result 来获取output 参数的值。如果有SELECT和输出参数,实际上意味着程序返回了几个结果集,你需要读取它们。

其实这里很similar question

编辑

如果我对您的理解正确,您想先检索OUTPUT 参数的值。然后你想检索SELECT 语句的结果。

据我所知,这是不可能的。 SQL Server 在第一个结果集中返回SELECT 语句的结果,您可以使用sqlsrv_fetch_array 读取该结果。然后使用sqlsrv_next_result 移至第二个结果集。此时OUTPUT 参数的值被传送到您的PHP 变量,因为SQL Server 在最后一个结果集中发送OUTPUT 参数的值。调用sqlsrv_next_result 后,当您调用sqlsrv_fetch_array 时,第一个结果集不再可用。

顺便说一句,当你说

奇怪的是,如果我创建的存储过程是 INSERT 或 UPDATE 语句输出正确返回。

,这很有意义。当存储过程没有SELECT 语句并且它有SET NOCOUNT ON; 时,返回的唯一结果集是OUTPUT 参数,因此您的PHP 变量被分配了正确的值,而无需显式调用sqlsrv_next_result

所以,你至少有两个选择。

1) 使用sqlsrv_fetch_arraySELECT 的完整结果读入一个临时数组。然后使用sqlsrv_next_result获取OUTPUT参数的值。然后根据接收到的OUTPUT参数值处理数组中保存的第一个SELECT的结果。

2) 根本不要使用OUTPUT 参数。使用此类参数,SQL Server 会隐式返回多个结果集。由于您有多个结果集,因此显式返回两个结果集。首先在您的存储过程中执行SELECT,它只返回一行和一个数字,然后是您的主要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);
}

【讨论】:

  • 非常感谢您的研究和帮助,$return 标签现在按预期正确返回为 1 或 0。我注意到我忘记提及我添加到我的问题中的一些重要内容,一旦返回检查返回,它将运行 while($row = sqlsrv_fetch_array($stmt)) { 取决于返回结果
  • 对不起,我不太明白。您现在收到的结果是否正确或仍有问题?
  • 抱歉,我收到了$return 的正确结果,但是我的选择语句中没有数据
  • 我编辑了答案并添加了更多细节和选项。
  • 弗拉达米尔!谢谢,这完美无缺,正是我想要实现的目标。感谢您的时间、精力和研究。当之无愧的赏金。最后一件事,&amp; 在参数之前是否有原因?
【解决方案2】:

这是由于 PHP 输出的字符编码问题造成的。尝试使用

header ('Content-type: text/html; charset=UTF-8');

header ('Content-type: text/html; charset=ISO-8859-1');

另外,您可以查看this nice article.

【讨论】:

  • 我已经按照您的建议尝试了这两个标题,并且字符更改如下。 UTF-8 - 0+��+。 ISO-8859-1 - 0ÄðÝÄ。我还尝试将 PHPTYPE 更改为 SQLSRV_PHPTYPE_INT
  • 存储过程排序规则是:Latin1_General_CI_AS 如果这有什么不同?
  • 运行echo mb_detect_encoding($return);时,回显的值为UTF-8
猜你喜欢
  • 2021-06-12
  • 2012-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多