【问题标题】:Procedure expects parameter which was not supplied过程需要未提供的参数
【发布时间】:2010-09-26 23:41:22
【问题描述】:

在 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 参数传递(我使用 @ 和而不是&)

【问题讨论】:

  • 非常老的问题,但我遇到了同样的问题,就我而言,我没有看到我在@parameter 之一中添加了额外的空格。一小时的调试。
  • 请参阅stackoverflow.com/a/26374810/1860652 以了解执行存储过程并收到此错误
  • 无论出于何种原因,这都出现在了今天的首页。但是,如果“模板”值来自客户端 URL,这听起来很容易受到 SQL 注入的影响!至少我会建议使用QUOTENAME(@template)

标签: .net sql-server stored-procedures


【解决方案1】:

除了这里的其他答案,如果你忘了放:

cmd.CommandType = CommandType.StoredProcedure;

那么你也会得到这个错误。

【讨论】:

  • 如果您从 Visual Studio 调试它:在报表的数据选项卡上 [除了布局和预览选项卡],在所选数据集名称旁边,还有另一个下拉控件可让您更改 CommandType。享受吧!
  • 是的,SqlException 很奇怪——它告诉你它知道它是一个过程,但是你必须设置它的 CommandType 属性来告诉它它是一个过程!
  • @Tahir,我认为错误更多的是使用“过程”作为通用术语(如添加“或函数”所暗示的那样),而不是暗示它知道意图是 SQL数据库存储过程。
  • 我想这是 99% 来这里的人的解决方案
  • 该死,为什么解决方案这么简单。谢谢你。我知道参数存在并且不为空,这就是全部。
【解决方案2】:

我会检查我的应用程序代码并查看您将@template 设置为什么值。我怀疑它是空的,这就是问题所在。

【讨论】:

  • 是的,模板为空,我之前忘记设置了。
  • 我可以补充一点 DbNull 是 C# 中最没用的“功能”
  • 还要检查代码和sp中的参数名称,它们可以不同。
【解决方案3】:

这个问题确实通常是由上面提到的 HLGEM 将参数值设置为 null 引起的。我想我会详细说明这个问题的一些解决方案,我发现这些解决方案对这个问题的新手很有用。

我更喜欢的解决方案是将存储过程参数默认为 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 有一篇很好的帖子 here,其中列出了处理该领域时一些更出色的扩展方法。

【讨论】:

    【解决方案4】:

    我遇到了一个问题,当我将 0 提供给整数参数时会出现错误。并发现:

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

    有效,但这不起作用:

    cmd.Parameters.Add(new SqlParameter("@Status", 0));
    

    【讨论】:

    • 第二个不起作用的原因是编译器认为您正在调用 SqlParameter 构造函数的 (string, SqlDbType) 重载。见备注here
    • 如果你想使用Add 语法,或者你正在为你的命令使用一个对象初始化器,你可以使用一个命名参数:cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
    【解决方案5】:

    对于我的情况,我必须从代​​码中传递 DBNULL.Value(使用 if else 条件)用于未定义 null 但值为 null 的存储过程参数。

    【讨论】:

      【解决方案6】:

      我在调用存储过程时遇到了类似的问题

      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
      

      这对我有用。

      我希望这对陷入类似陷阱的人有所帮助。

      【讨论】:

        【解决方案7】:

        如果没有设置模板(即==null),也会引发此错误。

        更多cmets:

        如果在添加参数的时候就知道参数值,也可以使用AddWithValue

        EXEC 不是必需的。可以直接在SELECT中引用@template参数。

        【讨论】:

          【解决方案8】:

          以防万一有人遇到,谷歌搜索。如果尽管定义了默认参数值(应该允许您在不显式传递该参数的情况下运行该过程)仍然出现此错误,请仔细检查您是否确实设置了默认值,如下所示:

          @someTextParameter NVARCHAR(100) NULL
          

          只让参数接受NULL 作为输入,而这个:

          @someTextParameter NVARCHAR(100) = NULL
          

          实际上是声明参数可选。

          这是一个愚蠢的错误,忽略了我花了半个小时;)

          【讨论】:

            【解决方案9】:

            我遇到了同样的问题。我的参数是空值。所以我通过处理空值来解决。如果有人不确定运行时值并且想要处理 null ,那么只需使用它。 (并且您不想更改 SP/功能。)例如

            sp.Value = Template ?? (object)DBNull.Value;
            

            【讨论】:

              【解决方案10】:

              首先 - 为什么是 EXEC?不应该只是

              AS
              SELECT Column_Name, ...
              FROM ...
              WHERE TABLE_NAME = @template
              

              当前的SP没有意义?特别是,这将寻找一个匹配@template 的column,而不是@template 的varchar 值。即如果@template 是'Column_Name',它将搜索WHERE TABLE_NAME = Column_Name,这是非常罕见的(表和列名称相同)。

              另外,如果您确实必须使用动态 SQL,则应使用EXEC sp_ExecuteSQL(将值保留为参数)以防止注入攻击(而不是输入连接)。但在这种情况下没有必要。

              实际问题 - 乍一看还可以;你确定你没有其他的 SP 副本吗?这是一个常见的错误...

              【讨论】:

              • 它仍然不适用于该更改。我有 exec 是因为我以前使用过一个 proc,其中 from 子句是从一个参数提供的,所以我对这个有错误的想法。但我仍然只是选择了错误
              • 很好奇;也许高音检查拼写错误?
              【解决方案11】:

              我今天在将空值传递给我的存储过程的参数时遇到了这个错误。我可以通过添加默认值 = null 来更改存储过程来轻松修复。

              【讨论】:

                【解决方案12】:

                我遇到了同样的问题,要解决它,只需在参数集合中添加与存储过程中完全相同的参数名称即可。

                例子

                假设您创建了一个存储过程:

                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 
                

                然后错误发生。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-02-05
                  • 2013-01-24
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多