프로 시저에 제공되지 않은 매개 변수가 필요합니다.


106

SQL Server에서 저장 프로 시저에 액세스 할 때 오류가 발생합니다.

Server Error in '/' Application.
Procedure or function 'ColumnSeek' expects parameter '@template', which was not supplied. 

이것은 매개 변수를 (System.data.SqlClient)제공하더라도 .net의 sql 데이터 연결을 통해 매개 변수가있는 저장 프로 시저를 호출 할 때 발생 합니다. 다음은 내 코드입니다.

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 매개 변수를 망 쳤기 때문에 템플릿이 null이었습니다 ( @대신 for 및 대신 사용 &)


아주 오래된 질문이지만 동일한 문제가 발생했으며 제 경우에는 @parameter 중 하나에 추가 공백을 추가 한 것을 보지 못했습니다. 1 시간의 디버깅.
Léon Pelletier 2012-06-16

저장 프로 시저를 실행하고이 오류를 가져 오는 방법 은 stackoverflow.com/a/26374810/1860652 를 참조하십시오
AlexFoxGill

이것은 어떤 이유로 든 오늘 첫 페이지에 게시되었습니다. 그러나 "템플릿"값이 클라이언트 URL에서 오는 경우 SQL 주입에 취약한 것처럼 들립니다! 적어도 나는 사용을 제안합니다QUOTENAME(@template)
Mark Sowul

답변:


85

내 응용 프로그램 코드를 확인하고 @template을 설정하는 값을 확인합니다. 나는 그것이 무효라고 생각하고 거기에 문제가 있습니다.


예, 템플릿이 null입니다. 이전에 설정하는 것을 잊었습니다.
Tony Peterson

35
난 그냥 DBNULL가 C 번호의 가장 쓸모없는 "기능"이라고 추가 할 수
thaBadDawg

295

여기에 다른 답변 외에도 다음을 입력하는 것을 잊은 경우 :

cmd.CommandType = CommandType.StoredProcedure;

그러면이 오류도 발생합니다.


Visual Studio에서 디버깅하는 경우 선택한 데이터 세트의 이름 옆에있는 보고서의 데이터 탭 [레이아웃 및 미리보기 탭 옆]에 CommandType을 변경할 수있는 또 다른 드롭 다운 컨트롤이 있습니다. 즐겨!
SarjanWebDev

2
예, SqlException은 이상합니다. 프로 시저로 알고 있다는 것을 알려주지 만 프로 시저임을 알리려면 CommandType 속성을 설정해야합니다!
Tahir Hassan

@Tahir, 나는 오류가 의도가 SQL DB 저장 프로 시저라는 것을 알고 있음을 암시하는 것보다 "프로 시저"를 일반 용어 ( "또는 함수"추가로 제 안됨)로 사용하는 것보다 더 많다고 생각합니다.
Brian

3
이것이 제가 상상하는 99 %의 사람들을위한 해결책입니다
Jonesopolis

젠장, 솔루션이 왜 이렇게 쉬웠습니까? 감사합니다. 매개 변수가 존재하고 null이 아니라는 것을 알았으며 이것이 필요한 전부였습니다.
BornToDoStuff 2019

27

이 문제는 실제로 위에서 언급 한 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);
}

매트 해밀턴은 좋은 게시물을 가지고 여기에 목록이 좀 더 큰 확장 방법이 지역을 처리 할 때있다.


12

정수 매개 변수에 0을 제공하면 오류가 발생하는 문제가 발생했습니다. 그리고 다음을 발견했습니다.

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

작동하지만 그렇지 않습니다.

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

6
두 번째가 작동하지 않는 이유는 컴파일러가 SqlParameter 생성자 의 (string, SqlDbType) 오버로드를 호출하고 있다고 생각하기 때문 입니다. 비고 참조여기에서 하십시오 .
Keith

1
사용하려는 경우 Add구문 거나 명령에 개체 이니셜 라이저를 사용하는 경우 명명 된 매개 변수를 사용할 수 있습니다.cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
user888734

7

제 경우 DBNULL.Value에는 정의되지 않은 저장 프로 시저 매개 변수에 대한 코드에서 (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;

여기에 저장 프로 시저의 입력 매개 변수를 다음과 같이 null로 초기화하고 있습니다.

    @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

그것은 나를위한 속임수였다.

비슷한 함정에 빠진 사람에게 도움이 되길 바랍니다.


3

템플릿이 설정되지 않은 경우 (즉 == null)이 오류도 발생합니다.

더 많은 의견 :

매개 변수를 추가 할 때까지 매개 변수 값을 알고 있으면 AddWithValue를 사용할 수도 있습니다.

EXEC는 필요하지 않습니다. SELECT에서 @template 매개 변수를 직접 참조 할 수 있습니다.


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의 다른 복사본이없는 것이 확실합니까? 이것은 일반적인 오류입니다 ...


그것은 여전히 ​​그 변화와 함께 작동하지 않습니다. 나는 이전에 from 절이 param에서 제공된 proc으로 작업했기 때문에 exec가 있었기 때문에 이것에 대해 잘못 생각했습니다. 하지만 여전히 선택만으로 오류가 발생합니다
Tony Peterson

매우 호기심이 많다. 오타에 대한 고음 검사?
Marc Gravell

0

내 저장 프로 시저의 매개 변수에 null 값이 전달되었을 때 오늘이 오류가 발생했습니다. 기본값 = null을 추가하여 저장 프로 시저를 변경하여 쉽게 수정할 수있었습니다.


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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.