У меня есть хранимая процедура в SQL Server 2012, которая сначала проверяет, существуют ли данные, если она делает, то устанавливает параметр Output @Return
в 1 и запускает запрос select, а если нет, то устанавливает @Return
в 0 и возвращает другой запрос выбора.
При тестировании этой хранимой процедуры, чтобы обеспечить точность данных, она идеально подходит и возвращает данные, которые я ожидаю. Проблема заключается на стороне PHP при попытке прочитать выходной параметр, показывающий t_rrr
когда он должен показывать 1 или 0. Я считаю, что проблема может быть в предопределенной константе в 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, но не могу получить желаемый результат. Странно, если я создаю хранимую процедуру, которая является оператором INSERT
или UPDATE
OUTPUT
возвращается правильно.
РЕДАКТИРОВАТЬ:
Сортировка хранимых процедур SQL Server - Latin1_General_CI_AS
У меня был установлен SQL Server и PHP на моем VirtualBox, поэтому я играл.
Немногие вещи:
&
при прохождении параметров.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
Когда у вас есть хранимая процедура, которая выполняет только INSERT
или UPDATE
, т.е. не возвращает результат SELECT
, тогда sqlsrv_next_result
не требуется для получения значений output
параметров. Если есть SELECT
вместе с выходными параметрами, это фактически означает, что процедура возвращает несколько наборов результатов, и вам нужно их прочитать.
Собственно, здесь очень похожий вопрос.
Если я правильно понял вас, вы хотите сначала получить значение параметра OUTPUT
. Затем вы хотите получить результат SELECT
.
Насколько я понимаю, это невозможно. SQL Server возвращает результат SELECT
в первом результирующем наборе, который вы можете прочитать с помощью sqlsrv_fetch_array
. Затем вы переходите во второй результирующий набор, используя sqlsrv_next_result
. В этот момент значение параметра OUTPUT
переносится в вашу переменную PHP, поскольку SQL Server отправляет значения параметров OUTPUT
в последний набор результатов. После вызова sqlsrv_next_result
первый набор результатов больше не доступен, когда вы выполняете sqlsrv_fetch_array
.
Кстати, когда вы сказали
Странно, если я создаю хранимую процедуру, которая является оператором INSERT или UPDATE, OUTPUT возвращается правильно.
это имеет смысл. Когда хранимая процедура не имеет SELECT
и имеет значение SET NOCOUNT ON;
, то единственным возвращаемым результатом является параметр OUTPUT
, поэтому ваша переменная PHP назначается с правильным значением без явных вызовов sqlsrv_next_result
.
Итак, у вас есть как минимум два варианта.
1) Прочитайте полный результат SELECT
во временном массиве, используя sqlsrv_fetch_array
. Затем используйте sqlsrv_next_result
чтобы получить значение параметра OUTPUT
. Затем обработайте результат первого SELECT
сохраненного в вашем массиве, на основе полученного значения параметра OUTPUT
.
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)) {
зависимости от результата возврата
Это связано с проблемой кодирования символов с выходом PHP. Попробуйте использовать
header ('Content-type: text/html; charset=UTF-8');
или
header ('Content-type: text/html; charset=ISO-8859-1');
Кроме того, вы можете проверить эту хорошую статью.
$return = 0;
вместо$return = "";
иSQLSRV_PARAM_INOUT
вместоSQLSRV_PARAM_OUT
иSQLSRV_PHPTYPE_INT(SQLSRV_ENC_BINARY)
вместоSQLSRV_PHPTYPE_STRING
.$return
как предложено,SQLRV_PARAM_INOUT
тип параметра наSQLRV_PARAM_INOUT
а PHPTYPE - наarray($return, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_INT,SQLSRV_SQLTYPE_INT),
теперь я всегда возвращаю 0 независимо от запроса (без глупых символов) иmb_detect_encoding
ASCII