Die Prozedur erwartet einen Parameter, der nicht angegeben wurde

106

Beim Zugriff auf eine gespeicherte Prozedur in SQL Server wird der Fehler angezeigt

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

Dies geschieht, wenn ich eine gespeicherte Prozedur mit einem Parameter über die Datenverbindung von .net zu SQL aufrufe (System.data.SqlClient), obwohl ich den Parameter bereitstelle . Hier ist mein Code.

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();

Und meine gespeicherte Prozedur ist:

   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);

Ich versuche herauszufinden, was ich hier falsch mache.

Bearbeiten: Wie sich herausstellte, war Template null, weil ich seinen Wert von einem Parameter erhalten habe, der über die URL übergeben wurde, und die URL-Parameterübergabe vermasselt habe (ich habe @für und anstelle von verwendet &).

Tony Peterson
quelle
Sehr alte Frage, aber ich bin auf dasselbe Problem gestoßen, und in meinem Fall habe ich nicht gesehen, dass ich einen zusätzlichen Platz in einem der @ -Parameter hinzugefügt habe. Eine Stunde Debuggen.
Léon Pelletier
Siehe stackoverflow.com/a/26374810/1860652 zum Ausführen gespeicherter Prozeduren und zum
Abrufen
Dies landete heute aus irgendeinem Grund auf der Titelseite. Es hört sich jedoch so an, als wäre dies anfällig für SQL-Injection, wenn der Wert "template" von der Client-URL stammt! Zumindest würde ich vorschlagen mitQUOTENAME(@template)
Mark Sowul

Antworten:

85

Ich würde meinen Anwendungscode überprüfen und sehen, auf welchen Wert Sie @template setzen. Ich vermute, es ist null und darin liegt das Problem.

HLGEM
quelle
Ja, Vorlage war null, ich habe vergessen, sie früher festzulegen.
Tony Peterson
35
Kann ich nur hinzufügen, dass DbNull das EINZIGE nutzloseste "Feature" von C # ist
thaBadDawg
295

Zusätzlich zu den anderen Antworten hier, wenn Sie vergessen haben zu setzen:

cmd.CommandType = CommandType.StoredProcedure;

Dann erhalten Sie auch diesen Fehler.

Brian
quelle
Wenn Sie es in Visual Studio debuggen: Auf der Registerkarte "Daten" des Berichts [neben den Registerkarten "Layout" und "Vorschau"] neben dem Namen des ausgewählten Datasets befindet sich ein weiteres Dropdown-Steuerelement, mit dem Sie den Befehlstyp ändern können. Genießen!
SarjanWebDev
2
Ja, die SqlException ist seltsam - sie sagt Ihnen, dass sie als Prozedur bekannt ist, aber dann müssen Sie die CommandType-Eigenschaft festlegen, um zu erkennen, dass es sich um eine Prozedur handelt!
Tahir Hassan
@ Tahir, ich denke, es ist eher so, dass der Fehler "procedure" als Oberbegriff verwendet (wie durch das Hinzufügen von "or function" vorgeschlagen), anstatt zu implizieren, dass die Absicht eine gespeicherte SQL DB-Prozedur ist.
Brian
3
Dies ist die Lösung für 99% der Leute, die hierher kommen, wie ich mir vorstellen kann
Jonesopolis
Verdammt, warum war die Lösung so einfach? Danke dir. Ich wusste, dass der Parameter existiert und nicht null ist und das war alles was es brauchte.
BornToDoStuff
27

Dieses Problem wird in der Tat normalerweise dadurch verursacht, dass ein Parameterwert wie oben erwähnt auf Null gesetzt wird. Ich dachte, ich würde einige Lösungen für dieses Problem erarbeiten, die ich zum Nutzen von Menschen, die neu in diesem Problem sind, als nützlich empfunden habe.

Die Lösung, die ich bevorzuge, besteht darin, die Parameter der gespeicherten Prozedur standardmäßig auf NULL (oder einen beliebigen Wert) zu setzen, was oben von Sangram erwähnt wurde , aber möglicherweise übersehen wird, weil die Antwort sehr ausführlich ist. Etwas in der Art von:

CREATE PROCEDURE GetEmployeeDetails
    @DateOfBirth    DATETIME = NULL,
    @Surname        VARCHAR(20),
    @GenderCode     INT = NULL,
AS

Dies bedeutet, dass, wenn der Parameter unter bestimmten Bedingungen im Code auf Null gesetzt wird, .NET den Parameter nicht setzt und die gespeicherte Prozedur den von ihr definierten Standardwert verwendet. Eine andere Lösung, wenn Sie das Problem wirklich im Code lösen möchten, wäre die Verwendung einer Erweiterungsmethode, die das Problem für Sie behandelt, etwa:

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 hat einen guten Beitrag hier , dass die Listen einige weitere große Erweiterungsmethoden , wenn sie mit diesem Bereich zu tun.

Xcalibur
quelle
12

Ich hatte ein Problem, bei dem ich den Fehler bekam, wenn ich einem ganzzahligen Parameter 0 gab. Und fand das:

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

funktioniert, aber das geht nicht:

cmd.Parameters.Add(new SqlParameter("@Status", 0));
Anders Rune Jensen
quelle
6
Der Grund, warum der zweite nicht funktioniert, ist, dass der Compiler denkt, Sie rufen die (Zeichenfolge, SqlDbType) Überladung des SqlParameter-Konstruktors auf. Siehe Anmerkungen hier .
Keith
1
Wenn Sie die AddSyntax verwenden cmd.Parameters.Add(new SqlParameter("@Status", value: 0));
möchten
7

Für meinen Fall musste ich DBNULL.Value(unter Verwendung der if else-Bedingung) Code für gespeicherte Prozedurparameter übergeben, die nicht definiert sind, nullaber der Wert ist null.

rafoo
quelle
5

Beim Aufrufen der gespeicherten Prozedur ist ein ähnliches Problem aufgetreten

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;

Welche dynamische Konstruktion der Suchabfrage habe ich oben aufgerufen von:

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;
    }

Dann habe ich nach viel Kopfkratzen die gespeicherte Prozedur geändert, um:

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;

HIER initialisiere ich die Eingabeparameter der gespeicherten Prozedur auf Null wie folgt

    @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

Das hat den Trick für mich getan.

Ich hoffe, dass dies für jemanden hilfreich ist, der in eine ähnliche Falle gerät.

Sangram
quelle
3

Wenn die Vorlage nicht festgelegt ist (dh == null), wird auch dieser Fehler ausgelöst.

Mehr Kommentare:

Wenn Sie den Parameterwert zum Zeitpunkt des Hinzufügens von Parametern kennen, können Sie auch AddWithValue verwenden

Die EXEC ist nicht erforderlich. Sie können den Parameter @template in SELECT direkt referenzieren.

devio
quelle
0

Erstens - warum ist das eine EXEC? Sollte das nicht einfach so sein?

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

Der aktuelle SP macht keinen Sinn? Dies würde insbesondere nach einer Spalte suchen, die mit @template übereinstimmt, nicht nach dem varchar-Wert von @template. dh wenn @template ist 'Column_Name', würde es suchen WHERE TABLE_NAME = Column_Name, was sehr selten ist (Tabelle und Spalte haben den gleichen Namen).

Auch wenn Sie keine haben dynamische SQL zu verwenden, sollten Sie verwenden EXEC sp_ExecuteSQL(wobei die Werte als Parameter) , um von Injection - Angriffe zu verhindern ( und nicht als Verkettung von Input). In diesem Fall ist dies jedoch nicht erforderlich.

Bezüglich des eigentlichen Problems - es sieht auf den ersten Blick in Ordnung aus; Sind Sie sicher, dass Sie keine andere Kopie des SP haben? Dies ist ein häufiger Fehler ...

Marc Gravell
quelle
Mit dieser Änderung funktioniert es immer noch nicht. Ich hatte die Exekutive, weil ich zuvor mit einem Proc gearbeitet habe, bei dem die from-Klausel von einem Parameter geliefert wurde, also habe ich falsch über diesen nachgedacht. Aber ich bekomme immer noch den Fehler nur mit der Auswahl
Tony Peterson
sehr neugierig; vielleicht dreifach nach Tippfehlern suchen?
Marc Gravell
0

Ich bin heute auf diesen Fehler gestoßen, als Nullwerte an die Parameter meiner gespeicherten Prozedur übergeben wurden. Ich konnte das Problem leicht beheben, indem ich die gespeicherte Prozedur durch Hinzufügen des Standardwerts = null änderte.

user4249282
quelle
0

Ich hatte das gleiche Problem, um es zu lösen, füge deiner Parametersammlung genau den gleichen Parameternamen hinzu wie in deinen gespeicherten Prozeduren.

Beispiel

Angenommen, Sie erstellen eine gespeicherte Prozedur:

create procedure up_select_employe_by_ID 
     (@ID int) 
as
    select * 
    from employe_t 
    where employeID = @ID

Benennen Sie Ihren Parameter also genau so, wie er sich in Ihrer gespeicherten Prozedur befindet

cmd.parameter.add("@ID", sqltype,size).value = @ID

wenn du gehst

cmd.parameter.add("@employeID", sqltype,size).value = @employeid 

dann passiert der Fehler.

Haitianischer Programmierer
quelle
0

Es muss angegeben werden, dass ein gespeicherter Prozess aufgerufen wird:

comm.CommandType = CommandType.StoredProcedure;
pp
quelle