У меня есть функция, которая вызывает хранимую процедуру с предоставлением ее параметров.
int8 CDBAgent::CreateKnights(uint16 sClanID, uint8 bNation, string & strKnightsName, string & strChief, uint8 bFlag)
{
int8 bRet = -1;
unique_ptr<OdbcCommand> dbCommand(m_GameDB->CreateCommand());
if (dbCommand.get() == nullptr)
return bRet;dbCommand->AddParameter(SQL_PARAM_OUTPUT, &bRet);
dbCommand->AddParameter(SQL_PARAM_INPUT, strKnightsName.c_str(), strKnightsName.length());
dbCommand->AddParameter(SQL_PARAM_INPUT, strChief.c_str(), strChief.length());
if (!dbCommand->Execute(string_format(_T("{? = CALL CREATE_KNIGHTS ( %d, %d, %d, ?, ?)}"), sClanID, bNation, bFlag)))
ReportSQLError(m_GameDB->GetError());return bRet;
}
и хранимая процедура;
USE [KN_online]
GO
/****** Object: StoredProcedure [dbo].[CREATE_KNIGHTS] Script Date: 09/04/2016 03:09:46 ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER ON
GO
-- Batch submitted through debugger: SQLQuery4.sql|7|0|C:\Users\Leth\AppData\Local\Temp\~vs3286.sql
/****** Object: Stored Procedure dbo.CREATE_KNIGHTS Script Date: 6/6/2006 6:03:32 PM ******/
-- modify by sungyong 2002.09.27
ALTER PROCEDURE [dbo].[CREATE_KNIGHTS]
@nRet smallint OUTPUT,
@index smallint,
@nation tinyint,
@community tinyint,
@strName char(21),
@strChief char(21)AS
DECLARE @Row tinyint, @knightsindex smallint, @knightsname char(21)
SET @Row = 0 SET @knightsindex = 0 SET @knightsname = ''
SELECT @Row = COUNT(*) FROM KNIGHTS WHERE IDNum = @index or IDName = @strName
IF @Row > 0 or @index = 0
BEGIN
SET @nRet = 3
RETURN
END
--SELECT @Row = COUNT(*) FROM KNIGHTS WHERE IDName = @strName
--IF @Row > 0
-- BEGIN
-- SET @nRet = 3
--RETURN
-- END
BEGIN TRAN
INSERT INTO KNIGHTS ( IDNum, Nation, Flag, IDName, Chief )
VALUES (@index, @nation, @community, @strName, @strChief )
INSERT INTO KNIGHTS_USER ( sIDNum, strUserID )
VALUES (@index, @strChief )
IF @@ERROR <> 0
BEGIN
ROLLBACK TRAN
SET @nRet = 6
RETURN
END
-- UPDATE USERDATA SET Knights = @index, Fame = 1 WHERE strUserId = @strChief -- 1 == Chief Authority
IF @@ERROR <> 0
BEGIN
ROLLBACK TRAN
SET @nRet = 6
RETURN
END
COMMIT TRAN
SET @nRet = 0
Проблема в том, что когда хранимая процедура вызывается, она передает значение sClanID в bRet, что я и нашел, выполнив инструкцию непосредственно на SQL-сервере (2008 R2), потому что, когда я выполняю напрямую подобное;
exec CREATE_KNIGHTS 15001, 2, 1, "OpenKO", 'test'
он пытается преобразовать char (OpenKO) в tinyint, и если я даю число как strName вместо OpenKO, он говорит, что процедура ожидает параметр @strChief, который не был предоставлен.
У меня есть другие функции с такой же структурой, и они прекрасно подходят для правильного выбора параметров и возврата результатов, но почему это не так? и как я могу решить это?
Если вам нравится, вы можете увидеть весь проект на GitHub
Для SEO на этот вопрос, как я упоминал ниже, если я ввожу sClanID в виде слов, он выдает ошибку «Ошибка преобразования типа данных char в tinyint», а если я ввожу числа, он дает «Ожидается параметр @statement, который не был указан» «
Лучше всего ставить объявление параметров OUTPUT всегда в конце и вызывать SP с именами параметров.
ALTER PROCEDURE [dbo].[CREATE_KNIGHTS]
@index smallint,
@nation tinyint,
@community tinyint,
@strName char(21),
@strChief char(21),
@nRet smallint OUTPUT
Следующее утверждение должно измениться так:
if (!dbCommand->Execute(string_format(_T("{CALL CREATE_KNIGHTS ( %d, %d, %d, ?, ?, ?)}"), sClanID, bNation, bFlag)))
И код должен быть таким;
dbCommand->AddParameter(SQL_PARAM_INPUT, strKnightsName.c_str(), strKnightsName.length());
dbCommand->AddParameter(SQL_PARAM_INPUT, strChief.c_str(), strChief.length());
dbCommand->AddParameter(SQL_PARAM_OUTPUT, &bRet);
Хотя мне удалось решить мой вопрос, я не приму мой собственный ответ, так как я ищу ответ в соответствии с его причиной.
Я решил это, просто передав первый параметр как bRet команде ODBC, так что последняя версия кода была;
int8 CDBAgent::CreateKnights(uint16 sClanID, uint8 bNation, string & strKnightsName, string & strChief, uint8 bFlag)
{
int8 bRet = -1;
unique_ptr<OdbcCommand> dbCommand(m_GameDB->CreateCommand());
if (dbCommand.get() == nullptr)
return bRet;dbCommand->AddParameter(SQL_PARAM_OUTPUT, &bRet);
dbCommand->AddParameter(SQL_PARAM_INPUT, strKnightsName.c_str(), strKnightsName.length());
dbCommand->AddParameter(SQL_PARAM_INPUT, strChief.c_str(), strChief.length());
if (!dbCommand->Execute(string_format(_T("{? = CALL CREATE_KNIGHTS (%d, %d, %d, %d, ?, ?)}"), bRet, sClanID, bNation, bFlag)))
ReportSQLError(m_GameDB->GetError());return bRet;
}
Поскольку проблема возникла из-за сдвига параметров при передаче значений в SP, просто передача начального значения переменной OUTPUT решила проблему.