Ответ 1
Я бы проверил код приложения и посмотрел, какое значение вы задаете для @template. Я подозреваю, что это нуль, и в этом проблема.
Я получаю сообщение об ошибке при доступе к хранимой процедуре в SQL Server
Server Error in '/' Application.
Procedure or function 'ColumnSeek' expects parameter '@template', which was not supplied.
Это происходит, когда я вызываю хранимую процедуру с параметром через .net-подключение данных к sql (System.data.SqlClient)
, хотя я поставляю параметр. Вот мой код.
SqlConnection sqlConn = new SqlConnection(connPath);
sqlConn.Open();
//METADATA RETRIEVAL
string sqlCommString = "QCApp.dbo.ColumnSeek";
SqlCommand metaDataComm = new SqlCommand(sqlCommString, sqlConn);
metaDataComm.CommandType = CommandType.StoredProcedure;
SqlParameter sp = metaDataComm.Parameters.Add("@template",SqlDbType.VarChar,50);
sp.Value = Template;
SqlDataReader metadr = metaDataComm.ExecuteReader();
И моя сохраненная процедура:
USE [QCApp]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ColumnSeek]
@template varchar(50)
AS
EXEC('SELECT Column_Name, Data_Type
FROM [QCApp].[INFORMATION_SCHEMA].[COLUMNS]
WHERE TABLE_NAME = ' + @template);
Я пытаюсь понять, что я делаю неправильно здесь.
Изменить: Как выясняется, шаблон был пустым, потому что я получал его значение от параметра, прошедшего через URL-адрес, и я напортачил с параметром url param (я использовал @
для и вместо &
)
Я бы проверил код приложения и посмотрел, какое значение вы задаете для @template. Я подозреваю, что это нуль, и в этом проблема.
В дополнение к другим ответам здесь, если вы забыли поставить:
cmd.CommandType = CommandType.StoredProcedure;
Затем вы также получите эту ошибку.
Эта проблема обычно вызвана установкой значения параметра равным null как HLGEM, упомянутого выше. Я думал, что буду подробно останавливаться на некоторых решениях этой проблемы, которые я нашел полезными в интересах людей, новых для этой проблемы.
Решение, которое я предпочитаю, заключается в том, что по умолчанию параметры хранимой процедуры равны NULL (или любое другое значение, которое вы хотите), о чем упоминалось выше sangram, но может быть пропущено, потому что ответ очень подробный. Что-то вроде:
CREATE PROCEDURE GetEmployeeDetails
@DateOfBirth DATETIME = NULL,
@Surname VARCHAR(20),
@GenderCode INT = NULL,
AS
Это означает, что если в некоторых случаях параметр будет установлен в код равным null,.NET не будет устанавливать этот параметр, а хранимая процедура будет использовать значение по умолчанию, которое оно определило. Другое решение, если вы действительно хотите решить проблему в коде, должно было бы использовать метод расширения, который обрабатывает проблему для вас, например:
public static SqlParameter AddParameter<T>(this SqlParameterCollection parameters, string parameterName, T value) where T : class
{
return value == null ? parameters.AddWithValue(parameterName, DBNull.Value) : parameters.AddWithValue(parameterName, value);
}
Мэтт Хэмилтон имеет хороший пост здесь, в котором перечислены некоторые более большие методы расширения при работе с этой областью.
У меня возникла проблема, когда я получил ошибку, когда я поставил 0 в целочисленный параметр. И обнаружил, что:
cmd.Parameters.AddWithValue("@Status", 0);
работает, но это не так:
cmd.Parameters.Add(new SqlParameter("@Status", 0));
Я сталкиваюсь с подобной проблемой при вызове хранимой процедуры
CREATE PROCEDURE UserPreference_Search
@UserPreferencesId int,
@SpecialOfferMails char(1),
@NewsLetters char(1),
@UserLoginId int,
@Currency varchar(50)
AS
DECLARE @QueryString nvarchar(4000)
SET @QueryString = 'SELECT UserPreferencesId,SpecialOfferMails,NewsLetters,UserLoginId,Currency FROM UserPreference'
IF(@UserPreferencesId IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE UserPreferencesId = @DummyUserPreferencesId';
END
IF(@SpecialOfferMails IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE SpecialOfferMails = @DummySpecialOfferMails';
END
IF(@NewsLetters IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE NewsLetters = @DummyNewsLetters';
END
IF(@UserLoginId IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE UserLoginId = @DummyUserLoginId';
END
IF(@Currency IS NOT NULL)
BEGIN
SET @QueryString = @QueryString + ' WHERE Currency = @DummyCurrency';
END
EXECUTE SP_EXECUTESQL @QueryString
,N'@DummyUserPreferencesId int, @DummySpecialOfferMails char(1), @DummyNewsLetters char(1), @DummyUserLoginId int, @DummyCurrency varchar(50)'
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected];
Какое динамическое построение запроса для поиска я вызывал выше одного:
public DataSet Search(int? AccessRightId, int? RoleId, int? ModuleId, char? CanAdd, char? CanEdit, char? CanDelete, DateTime? CreatedDatetime, DateTime? LastAccessDatetime, char? Deleted)
{
dbManager.ConnectionString = ConfigurationManager.ConnectionStrings["MSSQL"].ToString();
DataSet ds = new DataSet();
try
{
dbManager.Open();
dbManager.CreateParameters(9);
dbManager.AddParameters(0, "@AccessRightId", AccessRightId, ParameterDirection.Input);
dbManager.AddParameters(1, "@RoleId", RoleId, ParameterDirection.Input);
dbManager.AddParameters(2, "@ModuleId", ModuleId, ParameterDirection.Input);
dbManager.AddParameters(3, "@CanAdd", CanAdd, ParameterDirection.Input);
dbManager.AddParameters(4, "@CanEdit", CanEdit, ParameterDirection.Input);
dbManager.AddParameters(5, "@CanDelete", CanDelete, ParameterDirection.Input);
dbManager.AddParameters(6, "@CreatedDatetime", CreatedDatetime, ParameterDirection.Input);
dbManager.AddParameters(7, "@LastAccessDatetime", LastAccessDatetime, ParameterDirection.Input);
dbManager.AddParameters(8, "@Deleted", Deleted, ParameterDirection.Input);
ds = dbManager.ExecuteDataSet(CommandType.StoredProcedure, "AccessRight_Search");
return ds;
}
catch (Exception ex)
{
}
finally
{
dbManager.Dispose();
}
return ds;
}
Затем после многократного царапины головы я изменил хранимую процедуру на:
ALTER PROCEDURE [dbo].[AccessRight_Search]
@AccessRightId int=null,
@RoleId int=null,
@ModuleId int=null,
@CanAdd char(1)=null,
@CanEdit char(1)=null,
@CanDelete char(1)=null,
@CreatedDatetime datetime=null,
@LastAccessDatetime datetime=null,
@Deleted char(1)=null
AS
DECLARE @QueryString nvarchar(4000)
DECLARE @HasWhere bit
SET @HasWhere=0
SET @QueryString = 'SELECT a.AccessRightId, a.RoleId,a.ModuleId, a.CanAdd, a.CanEdit, a.CanDelete, a.CreatedDatetime, a.LastAccessDatetime, a.Deleted, b.RoleName, c.ModuleName FROM AccessRight a, Role b, Module c WHERE a.RoleId = b.RoleId AND a.ModuleId = c.ModuleId'
SET @HasWhere=1;
IF(@AccessRightId IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.AccessRightId = @DummyAccessRightId';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.AccessRightId = @DummyAccessRightId';
END
IF(@RoleId IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.RoleId = @DummyRoleId';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.RoleId = @DummyRoleId';
END
IF(@ModuleId IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.ModuleId = @DummyModuleId';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.ModuleId = @DummyModuleId';
END
IF(@CanAdd IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.CanAdd = @DummyCanAdd';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.CanAdd = @DummyCanAdd';
END
IF(@CanEdit IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.CanEdit = @DummyCanEdit';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.CanEdit = @DummyCanEdit';
END
IF(@CanDelete IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.CanDelete = @DummyCanDelete';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.CanDelete = @DummyCanDelete';
END
IF(@CreatedDatetime IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.CreatedDatetime = @DummyCreatedDatetime';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.CreatedDatetime = @DummyCreatedDatetime';
END
IF(@LastAccessDatetime IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.LastAccessDatetime = @DummyLastAccessDatetime';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.LastAccessDatetime = @DummyLastAccessDatetime';
END
IF(@Deleted IS NOT NULL)
BEGIN
IF(@HasWhere=0)
BEGIN
SET @QueryString = @QueryString + ' WHERE a.Deleted = @DummyDeleted';
SET @HasWhere=1;
END
ELSE SET @QueryString = @QueryString + ' AND a.Deleted = @DummyDeleted';
END
PRINT @QueryString
EXECUTE SP_EXECUTESQL @QueryString
,N'@DummyAccessRightId int, @DummyRoleId int, @DummyModuleId int, @DummyCanAdd char(1), @DummyCanEdit char(1), @DummyCanDelete char(1), @DummyCreatedDatetime datetime, @DummyLastAccessDatetime datetime, @DummyDeleted char(1)'
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected]
,@[email protected];
ЗДЕСЬ Я инициализирую входные пары хранимой процедуры нулевым, как указано ниже
@AccessRightId int=null,
@RoleId int=null,
@ModuleId int=null,
@CanAdd char(1)=null,
@CanEdit char(1)=null,
@CanDelete char(1)=null,
@CreatedDatetime datetime=null,
@LastAccessDatetime datetime=null,
@Deleted char(1)=null
который сделал трюк для Меня.
Я надеюсь, что это будет полезно для тех, кто попадает в подобную ловушку.
В моем случае мне пришлось передать DBNULL.Value
(используя условие else) из кода для параметра хранимых процедур, которые не определены null
, но значение null
.
Если шаблон не установлен (то есть == null), эта ошибка также будет поднята.
Другие комментарии:
Если вы знаете значение параметра к моменту добавления параметров, вы также можете использовать AddWithValue
EXEC не требуется. Вы можете напрямую ссылаться на параметр @template в SELECT.
Во-первых - почему это EXEC? Разве это не должно быть
AS
SELECT Column_Name, ...
FROM ...
WHERE TABLE_NAME = @template
Текущий SP не имеет смысла? В частности, это будет искать столбец, соответствующий @template, а не значение varchar @template. то есть, если @template является 'Column_Name'
, он будет искать WHERE TABLE_NAME = Column_Name
, что очень редко (для того, чтобы таблица и столбец совпадали).
Кроме того, если вам нужно использовать динамический SQL, вы должны использовать EXEC sp_ExecuteSQL
(сохраняя значения как параметры), чтобы предотвратить атаки на инъекции (а не конкатенацию ввода). Но в этом случае это не обязательно.
Реальная проблема - это выглядит хорошо с первого взгляда; вы уверены, что у вас нет другой копии SP, висящей вокруг? Это обычная ошибка...
У меня была одна и та же проблема: для ее решения просто добавьте точно такое же имя параметра в коллекцию параметров, как и в ваших хранимых процедурах.
Пример
Предположим, вы создали хранимую процедуру:
create procedure up_select_employe_by_ID
(@ID int)
as
select *
from employe_t
where employeID = @ID
Поэтому не забудьте указать свой параметр точно так же, как в хранимой процедуре, это будет
cmd.parameter.add("@ID", sqltype,size).value = @ID
если вы идете
cmd.parameter.add("@employeID", sqltype,size).value = @employeid
то произойдет ошибка.
Я столкнулся с этой ошибкой сегодня, когда нулевые значения были переданы моим параметрам хранимой процедуры. Я смог легко исправить, изменив хранимую процедуру, добавив значение по умолчанию = null.
Необходимо сказать, что хранимый процесс вызывается:
comm.CommandType = CommandType.StoredProcedure;