执行ExecuteNonQuery()存储过程时返回-1

13

我正尝试在Visual Studio中执行存储过程,它如下所示。

CREATE PROCEDURE [dbo].[addStudent] 
    @stuName varchar(50), 
    @address varchar(100),
    @tel varchar(15),
    @etel varchar(15),
    @nic varchar (10),
    @dob date


AS 
BEGIN   
    SET NOCOUNT ON;

    DECLARE @currentID INT
    DECLARE @existPerson INT
    SET @existPerson = (SELECT p_ID FROM Student WHERE s_NIC = @nic);
    IF @existPerson = null
        BEGIN
            INSERT INTO Person (p_Name, p_RegDate, p_Address, p_Tel, p_EmergeNo, p_Valid, p_Userlevel)
            VALUES (@stuName,  GETDATE(), @address, @tel, @etel, 0, 'Student' );
            SET @currentID = (SELECT MAX( p_ID) FROM Person); 
            INSERT INTO Student (p_ID, s_Barcode, s_DOB, s_NIC) VALUES (@currentID , NULL, @dob, @nic);
            return 0;
        END
    ELSE
        return -1;
END 

我是通过使用下面的代码来实现的。

        SqlConnection con = new SqlConnection();
        Connect conn = new Connect();
        con = conn.getConnected();
        con.Open();
        cmd = new SqlCommand("addStudent", con);
        cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add("@stuName", SqlDbType.VarChar).Value = nameTxt.Text.ToString();
                cmd.Parameters.Add("@address", SqlDbType.VarChar).Value = addressTxt.Text.ToString();
                cmd.Parameters.Add("@tel", SqlDbType.VarChar).Value = telTxt.Text.ToString();
                cmd.Parameters.Add("@etel", SqlDbType.VarChar).Value = emerTxt.Text.ToString();
                cmd.Parameters.Add("@nic", SqlDbType.VarChar).Value = nicTxt.Text.ToString();
                cmd.Parameters.Add("@dob", SqlDbType.DateTime).Value = dobTime.Value.ToString("MM-dd-yyyy");

                    int n = cmd.ExecuteNonQuery();
                    MessageBox.Show(n.ToString());

但是它返回给我-1。我尝试了通过输入我从调试中捕获的相同值来执行此存储过程。它成功了。可能出现什么错误? 非常感谢!


尝试将@dob作为Datetime而不是Date。因为在C#代码中,@dob是SqlDbType.Datetime。相应地进行更改,看看是否有帮助? - muhammad kashif
不要使用 SET @currentID = (SELECT MAX) 这一行,尝试使用 @@IDENTITY。虽然这与错误无关,但这只是一个提示。 - SchmitzIT
你应该真正使用 yyyyMMdd 格式来表示日期,而不是 MM-dd-yyyy 或类似的格式,这样更加跨文化。 - Seph
@Seph - 他们应该将它们作为“DateTime”传递,而不是完全将它们格式化为字符串。 - Damien_The_Unbeliever
3个回答

17

不要使用= null,使用is null

IF @existPerson is null

当你将任何东西与= null进行比较时,结果总是false(除非你已经关闭了ansi_nulls,但是不应该这样做,因为这个选项已被弃用)

更好的方式是使用

IF NOT EXISTS (SELECT p_ID FROM Student WHERE s_NIC = @nic)

另外,你应该使用SCOPE_IDENTITY()而不是SET @currentID = (SELECT MAX( p_ID) FROM Person);

SET @currentID = SCOPE_IDENTITY()

最后,您还需要添加一个参数来收集返回值。

   SqlParameter retValue = cmd.Parameters.Add("return", SqlDbType.Int);
   retValue.Direction = ParameterDirection.ReturnValue;

那么

MessageBox.Show(retValue.Value);

12
让我们来看一下 ExecuteNonQuery 的文档:
对于 UPDATE、INSERT 和 DELETE 语句,返回值是命令所影响的行数。…… 对于所有其他类型的语句,返回值均为 -1。
你正在调用存储过程,它本身并不属于列出的三种返回行计数的语句之一。
如果您想确定存储过程中传递给 return 语句的值,您需要向命令添加另一个参数,并将其 Direction 属性设置为 ReturnValue(此参数的名称将被忽略)。

3
如果存储过程执行更新、插入或删除操作,则如果它由ExecuteNonQuery调用,它将返回此命令受影响的行数。在这种情况下,存储过程不被视为语句,而其中的语句则是如此。 - Tom Heard
1
SO不让我取消点赞,所以我在评论中说明——这个答案是错误的。我认为它是正确的原因是因为我在存储过程中使用了"SET NOCOUNT ON"。但是当我像@Ahitosh的答案所说的那样使用"SET NOCOUNT OFF"时,ExecuteNonQuery()会正确返回在我的存储过程中更新的受影响行数。 - mulllhausen
正确的答案。但不必要刻薄。我能理解这可能会令人困惑。假设我有一个只执行其中一种操作而没有其他操作的存储过程?我很容易看出这可能被误认为是一个“错误”。 - pim

10

要解决这个问题,只需删除 "SET NOCOUNT ON" 或将其更改为 "SET NOCOUNT OFF"。这样一切都可以正常工作!


它对我有所帮助,如下: 1)SET NOCOUNT ON 表示 T-SQL 影响的行数不会被返回 2)SET NOCOUNT OFF 表示 T-SQL 影响的行数将被返回。 - Denis Evseev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接