Процедура ожидает параметр, который не был указан

106

Я получаю сообщение об ошибке при доступе к хранимой процедуре в 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-адреса (я использовал @для и вместо &)

Тони Петерсон
источник
Очень старый вопрос, но я столкнулся с той же проблемой, и в моем случае я не заметил, что добавил дополнительное пространство в один из параметров @. Один час отладки.
Леон Пеллетье
См. Stackoverflow.com/a/26374810/1860652 для выполнения хранимых процедур и получения этой ошибки
AlexFoxGill
Сегодня по какой-то причине это оказалось на первой полосе. Но похоже, что это уязвимо для SQL-инъекции, если значение «шаблона» исходит из клиентского URL! По крайней мере, я бы предложил использоватьQUOTENAME(@template)
Марк Совул

Ответы:

85

Я бы проверил код своего приложения и посмотрел, какое значение вы устанавливаете для @template. Я подозреваю, что это ноль, и в этом проблема.

HLGEM
источник
Да, шаблон был нулевым, я забыл установить его ранее.
Тони Петерсон
35
Могу я просто добавить, что DbNull - ЕДИНСТВЕННАЯ самая бесполезная «особенность» C #
thaBadDawg
295

В дополнение к другим ответам здесь, если вы забыли указать:

cmd.CommandType = CommandType.StoredProcedure;

Тогда вы также получите эту ошибку.

Брайан
источник
Если вы отлаживаете его из Visual Studio: на вкладке данных отчета [рядом с вкладками макета и предварительного просмотра] рядом с именем выбранного набора данных есть еще один раскрывающийся элемент управления, который позволяет вам изменить CommandType. Наслаждайтесь!
SarjanWebDev
2
да, SqlException странный - он говорит вам, что знает его как процедуру, но затем вы должны установить его свойство CommandType, чтобы сообщить ему, что это процедура!
Тахир Хассан
@Tahir, я думаю, что ошибка заключается в использовании термина «процедура» в качестве общего термина (как предлагается добавлением «или функции»), а не в том, что он осознает, что намерение является хранимой процедурой базы данных SQL.
Брайан,
3
это решение для 99% людей, приезжающих сюда, я полагаю
Jonesopolis
Черт возьми, почему решение было таким простым. Спасибо. Я знал, что параметр существует и не был нулевым, и это было все, что требовалось.
BornToDoStuff
27

Эта проблема действительно обычно возникает из-за установки значения параметра равным 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);
}

Matt Hamilton имеет хороший пост здесь , что списки еще некоторые большие методы расширения при работе с этой областью.

Xcalibur
источник
12

У меня была проблема, когда я получал ошибку, когда я указывал 0 для целочисленного параметра. И обнаружил, что:

cmd.Parameters.AddWithValue("@Status", 0);

работает, но это не так:

cmd.Parameters.Add(new SqlParameter("@Status", 0));
Андерс Руне Йенсен
источник
6
Причина, по которой второй не работает, заключается в том, что компилятор считает, что вы вызываете перегрузку (string, SqlDbType) конструктора SqlParameter. См. Примечания здесь .
Кейт
1
Если вы хотели использовать Addсинтаксис или использовали инициализатор объекта для своей команды, вы можете использовать именованный параметр:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734
7

В моем случае мне пришлось передать DBNULL.Value(используя условие if else) из кода для параметра хранимых процедур, который не определен, nullно есть значение null.

рафу
источник
5

Я столкнулся с аналогичной проблемой при вызове хранимой процедуры

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)'
                     ,@DummyUserPreferencesId=@UserPreferencesId
                     ,@DummySpecialOfferMails=@SpecialOfferMails
                     ,@DummyNewsLetters=@NewsLetters
                     ,@DummyUserLoginId=@UserLoginId
                     ,@DummyCurrency=@Currency;

Который динамически строил запрос для поиска, который я вызывал выше:

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)'
                      ,@DummyAccessRightId=@AccessRightId
                      ,@DummyRoleId=@RoleId
                      ,@DummyModuleId=@ModuleId
                      ,@DummyCanAdd=@CanAdd
                      ,@DummyCanEdit=@CanEdit
                      ,@DummyCanDelete=@CanDelete
                      ,@DummyCreatedDatetime=@CreatedDatetime
                      ,@DummyLastAccessDatetime=@LastAccessDatetime
                      ,@DummyDeleted=@Deleted;

ЗДЕСЬ я инициализирую входные параметры хранимой процедуры до нуля, как следует

    @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

Это помогло Мне.

Я надеюсь, что это будет полезно тем, кто попадется в подобную ловушку.

Sangram
источник
3

Если шаблон не установлен (т.е. == null), эта ошибка также будет вызвана.

Больше комментариев:

Если вы знаете значение параметра к моменту добавления параметров, вы также можете использовать AddWithValue

EXEC не требуется. Вы можете напрямую ссылаться на параметр @template в SELECT.

девио
источник
0

Во-первых, почему это 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? Это частая ошибка ...

Марк Гравелл
источник
Это все еще не работает с этим изменением. У меня был exec, потому что я раньше работал с процедурой, где предложение from было предоставлено из параметра, поэтому я ошибочно подумал об этом. Но я все еще получаю ошибку только с выбором
Тони Петерсон
очень любопытный; возможно тройная проверка на опечатки?
Марк Гравелл
0

Я столкнулся с этой ошибкой сегодня, когда в параметры моей хранимой процедуры были переданы нулевые значения. Мне удалось легко исправить, изменив хранимую процедуру, добавив значение по умолчанию = null.

user4249282
источник
0

У меня была такая же проблема, чтобы решить ее, просто добавьте точно такое же имя параметра в свою коллекцию параметров, что и в ваших хранимых процедурах.

пример

Допустим, вы создали хранимую процедуру:

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 

тогда происходит ошибка.

Гаитянский программист
источник
0

Необходимо сказать, что вызывается Stored Proc:

comm.CommandType = CommandType.StoredProcedure;
pp
источник