【问题标题】:Procedure or function !!! has too many arguments specified过程或功能!!!指定的参数过多
【发布时间】:2013-06-21 23:26:43
【问题描述】:

我正在 SQL Server 2008 中开发我的第一个存储过程,需要有关错误消息的建议。

过程或函数 xxx 指定的参数过多

我在执行存储过程[dbo].[M_UPDATES] 后得到它,它调用另一个名为etl_M_Update_Promo 的存储过程。

当通过鼠标右键单击和“执行存储过程”调用[dbo].[M_UPDATES](代码见下文)时,查询窗口中出现的查询是:

USE [Database_Test]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[M_UPDATES]

SELECT  'Return Value' = @return_value

GO

输出是

消息 8144,级别 16,状态 2,过程 etl_M_Update_Promo,第 0 行
过程或函数 etl_M_Update_Promo 指定的参数过多。

问题:此错误消息的确切含义是什么,即哪里有太多参数?如何识别它们?

我发现有几个线程询问此错误消息,但提供的代码与我的完全不同(如果不是像 C# 这样的另一种语言)。所以没有一个答案解决了我的SQL查询(即SP)的问题。

注意:下面我提供了用于两个 SP 的代码,但我更改了数据库名称、表名称和列名称。所以,请不要担心命名约定,这些只是示例名称!

(1) SP1 [dbo].[M_UPDATES] 的代码

USE [Database_Test]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[ M_UPDATES] AS
declare @GenID bigint
declare @Description nvarchar(50)

Set @GenID = SCOPE_IDENTITY()
Set @Description = 'M Update'

BEGIN
EXEC etl.etl_M_Update_Promo @GenID, @Description
END

GO

(2) SP2 的代码 [etl_M_Update_Promo]

USE [Database_Test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [etl].[etl_M_Update_Promo]
@GenId bigint = 0
as

declare @start datetime = getdate ()
declare @Process varchar (100) = 'Update_Promo'
declare @SummeryOfTable TABLE (Change varchar (20))
declare @Description nvarchar(50)
declare @ErrorNo int
, @ErrorMsg varchar (max)
declare @Inserts int = 0
, @Updates int = 0
, @Deleted int = 0
, @OwnGenId bit = 0

begin try


if @GenId = 0 begin
INSERT INTO Logging.dbo.ETL_Gen (Starttime)
VALUES (@start)

SET @GenId = SCOPE_IDENTITY()
SET @OwnGenId = 1
end


MERGE [Database_Test].[dbo].[Promo] AS TARGET
USING OPENQUERY( M ,'select * from m.PROMO' ) AS SOURCE 
ON (TARGET.[E] = SOURCE.[E]) 


WHEN MATCHED AND  TARGET.[A] <> SOURCE.[A]
  OR TARGET.[B] <> SOURCE.[B]
  OR TARGET.[C] <> SOURCE.[C]
  THEN 
UPDATE SET TARGET.[A] = SOURCE.[A]
  ,TARGET.[B] = SOURCE.[B]
  , TARGET.[C] = SOURCE.[c]

WHEN NOT MATCHED BY TARGET THEN 
INSERT ([E]
  ,[A]
  ,[B]
  ,[C]
  ,[D]
  ,[F]
  ,[G]
  ,[H]
  ,[I]
  ,[J]
  ,[K]
  ,[L]  
  ) 
VALUES (SOURCE.[E]
  ,SOURCE.[A]
  ,SOURCE.[B]
  ,SOURCE.[C]
  ,SOURCE.[D]
  ,SOURCE.[F]
  ,SOURCE.[G]
  ,SOURCE.[H]
  ,SOURCE.[I]
  ,SOURCE.[J]
  ,SOURCE.[K]
  ,SOURCE.[L]
)

OUTPUT $ACTION  INTO @SummeryOfTable; 


with cte as (
SELECT
Change,
COUNT(*) AS CountPerChange
FROM @SummeryOfTable
GROUP BY Change
)

SELECT
@Inserts =
    CASE Change
        WHEN 'INSERT' THEN CountPerChange ELSE @Inserts
    END,
@Updates =
    CASE Change
        WHEN 'UPDATE' THEN CountPerChange ELSE @Updates
    END,
@Deleted =
    CASE Change
        WHEN 'DELETE' THEN CountPerChange ELSE @Deleted
    END
FROM cte


INSERT INTO Logging.dbo.ETL_log (GenID, Startdate, Enddate, Process, Message, Inserts, Updates, Deleted,Description)
VALUES (@GenId, @start, GETDATE(), @Process, 'ETL succeded', @Inserts, @Updates,     @Deleted,@Description)


if @OwnGenId = 1
UPDATE Logging.dbo.ETL_Gen
SET Endtime = GETDATE()
WHERE ID = @GenId

end try
begin catch

SET @ErrorNo = ERROR_NUMBER()
SET @ErrorMsg = ERROR_MESSAGE()

INSERT INTO Logging.dbo.ETL_Log (GenId, Startdate, Enddate, Process, Message, ErrorNo, Description)
VALUES (@GenId, @start, GETDATE(), @Process, @ErrorMsg, @ErrorNo,@Description)


end catch
GO

【问题讨论】:

    标签: sql database sql-server-2008 tsql stored-procedures


    【解决方案1】:

    您使用 2 个参数(@GenId 和 @Description)调用该函数:

    EXEC etl.etl_M_Update_Promo @GenID, @Description
    

    但是你已经声明函数接受 1 个参数:

    ALTER PROCEDURE [etl].[etl_M_Update_Promo]
        @GenId bigint = 0
    

    SQL Server 告诉你[etl_M_Update_Promo] 只需要 1 个参数 (@GenId)

    您可以通过指定@Description 来更改过程以采用两个参数。

    ALTER PROCEDURE [etl].[etl_M_Update_Promo]
        @GenId bigint = 0,
        @Description NVARCHAR(50)
    AS 
    
    .... Rest of your code.
    

    【讨论】:

    • 太棒了!这在从声明部分删除 @Description NVARCHAR(50) 后有效。感谢您如此快速而清晰地回复!
    • @Darren 是否有忽略意外参数的机制?
    • @AliAdlavaran 添加默认值
    【解决方案2】:

    在定义它们之前使用以下命令:

    cmd.Parameters.Clear()
    

    【讨论】:

      【解决方案3】:

      此答案基于标题,而不是原始帖子中的具体案例。

      我有一个插入过程不断抛出这个烦人的错误,即使错误说“过程....指定了太多参数”,但事实是该过程没有足够的参数。

      该表有一个增量 id 列,由于它是增量的,我没有费心将它作为变量/参数添加到 proc 中,但事实证明它是必需的,所以我将它添加为 @Id 和中提琴就像他们说的那样......它有效。

      【讨论】:

      • 我也有同样的情况。将默认 = 0 的 @ID 添加到过程中,但 VBA 对象没有在其调用中使用它,也没有在 INSERT 中使用它,但是是的,它现在工作正常。有趣的是,当我在 SQL 中运行 EXEC 时它很好,但从我的 VBA 项目中调用时就不行了。一定是个bug!
      【解决方案4】:

      对于那些可能和我有同样问题的人来说,当我使用的数据库实际上是 ma​​ster 而不是我应该使用的数据库时,我收到了这个错误。

      只需将use [DBName] 放在脚本顶部,或在 SQL Server Management Studio GUI 中手动更改正在使用的数据库。

      【讨论】:

        【解决方案5】:

        此错误的另一个原因是当您从代码中调用存储过程时,代码中的参数类型与存储过程上的类型不匹配。

        【讨论】:

          【解决方案6】:

          除了到目前为止提供的所有答案之外,当您使用 ADO.Net 将数据从列表保存到数据库时,可能会发生导致此异常的另一个原因。

          许多开发人员会错误地使用for 循环或foreach 并让SqlCommand 在循环外执行,以避免发生这种情况,请确保您喜欢以下代码示例:

          public static void Save(List<myClass> listMyClass)
              {
                  using (var Scope = new System.Transactions.TransactionScope())
                  {
                      if (listMyClass.Count > 0)
                      {
                          for (int i = 0; i < listMyClass.Count; i++)
                          {
                              SqlCommand cmd = new SqlCommand("dbo.SP_SaveChanges", myConnection);
                              cmd.CommandType = CommandType.StoredProcedure;
                              cmd.Parameters.Clear();
          
                              cmd.Parameters.AddWithValue("@ID", listMyClass[i].ID);
                              cmd.Parameters.AddWithValue("@FirstName", listMyClass[i].FirstName);
                              cmd.Parameters.AddWithValue("@LastName", listMyClass[i].LastName);
          
                              try
                              {
                                  myConnection.Open();
                                  cmd.ExecuteNonQuery();
                              }
                              catch (SqlException sqe)
                              {
                                  throw new Exception(sqe.Message);
                              }
                              catch (Exception ex)
                              {
                                  throw new Exception(ex.Message);
                              }
                              finally
                              {
                                  myConnection.Close();
                              }
                          }
                      }
                      else
                      {
                          throw new Exception("List is empty");
                      }
          
                      Scope.Complete();
                  }
              }
          

          【讨论】:

          • 如果我可以建议对此代码进行一些改进... SqlCommandIDisposable 所以应该在 using 块内。您可能想阅读can we stop using AddWithValue。这不是线程安全的,因为myConnection 变量不属于此方法,所以我希望它被正确处理(因为它也是IDisposable)。如果是这样,那么您也可以将其放入 using 块中,这样就不需要 finally 块。还有……
          • ... try...catch...rethrow-as-Exception 意味着调用代码将无法确定发生了什么样的问题,并且这些异常的所有细节都将丢失,因为正在传递的唯一信息是异常的消息部分。这种设计是在循环内打开和关闭连接,由于连接池,这不会太糟糕;但可以通过在循环外打开和关闭连接来避免。无需清除参数,因为cmd 之前仅创建了两行。还有……
          • ...最后,考虑使用foreach,而不是for 循环;因为它使代码更易于阅读,而且速度会更快,因为它不必每次通过循环进行三个索引器查找:listMyClass[i]
          【解决方案7】:

          我什至为不得不发布此内容而感到羞耻,但它可能对将来的某人有所帮助。确保您的函数调用中没有拼写错误!

          我在尝试调用函数时不断收到此错误,但无法弄清楚原因。我的函数和调用有相同数量的参数(或者我是这么认为的)。

          这是我的函数调用:

          SELECT FORMAT_NAME(A.LASTNAME, A.FIRSTNAME, A,MIDDLENAME)

          在 Stack Overflow 中更容易看到,但在 SSMS 中不是很明显,A.MIDDLENAME 用逗号代替了句点。

          SELECT FORMAT_NAME(A.LASTNAME, A.FIRSTNAME, A.MIDDLENAME)

          简单的用户错误。

          【讨论】:

            猜你喜欢
            • 2023-03-26
            • 2014-02-08
            • 2015-12-10
            相关资源
            最近更新 更多