如何将 ExecuteScalar 与存储过程一起使用?

How do I use ExecuteScalar with a stored Procedure?

我正在尝试获取 Sql 数据库中列记录的计数并将结果显示在 MessageBox 中。

这是我的代码:

public DataTable CheckIfNameExist(string name)
{
    con = Connect();
    cmd = new SqlCommand();
    cmd.Connection = con;
    cmd.CommandText = "spCheckIfNameExist";
    cmd.Parameters.AddWithValue("@Name", SqlDbType.NVarChar).Value = name;
    MessageBox.Show(name);

    Int32 totalNames = (Int32) cmd.ExecuteScalar();
    string tNames = totalNames.ToString();

    MessageBox.Show(tNames);
}

这是我的sp:

@Name nvarchar(50) = null
As
Begin
SELECT COUNT(*) from OrdersSent where CustomerName LIKE @Name + '%'
End

问题:

总是returns0.

您需要像这样指定命令的类型:

cmd.CommandText = "spCheckIfNameExist";
cmd.CommandType = CommandType.StoredProcedure;

另请参阅:

What is the benefit of using CommandType.StoredProcedure versus using CommandType.Text?

虽然直接指定类型并使用 Value 属性 比 AddWithValue 更好:

cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = name;

以下文章也可能很有趣:

https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/

你的 C# 代码有一些问题 - 最重要的可能是这个:

cmd.Parameters.AddWithValue("@Name", SqlDbType.NVarChar).Value = name;

Don't use AddWithValue. 使用 Add

此外,您没有指定命令类型 - 默认为 Text
而且您正在使用 SqlConnectionSqlCommand 的字段 - 这也是错误的做法。您应该在使用它们的每个方法中创建和处理它们。

您的代码的更好版本是:

using(var con = new SqlConnection(ConnectionString))
{
    using(var cmd = new SqlCommand("spCheckIfNameExist", con))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = name;
        con.Open();
        var tNames = cmd.ExecuteScalar().ToString();
    }
}

另一件让我困惑的事情是为什么一个名为 CheckIfNameExist return 的方法是 DataTable。我希望它只是 return a bool.

如果你真的只想检查名称是否存在,你可以在 SQL 级别和 c# 级别上做得更好。

更好的SQL应该是这样的:

SELECT CAST(CASE WHEN EXISTS(
    SELECT 1 
    FROM OrdersSent 
    WHERE CustomerName LIKE @Name + '%'
) THEN 1 ELSE 0 END AS bit)

而在 c# 级别,位直接转换为 bool,因此代码可以简单为:

public bool CheckIfNameExist(string name)
{
    using(var con = new SqlConnection(ConnectionString))
    {
        using(var cmd = new SqlCommand("spCheckIfNameExist", con))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = name;
            con.Open();
            return (bool)cmd.ExecuteScalar();
        }
    }
}

还有一条注意事项 - 您应该避免对存储过程使用 sp 前缀。
Microsoft 已为内置系统过程保留此前缀。 有关详细信息,请阅读 Aaron Bertrand 的 Is the sp_ prefix still a no-no?,您会在其中看到此问题的简短答案是 "Yes"。

The sp_ prefix does not mean what you think it does: most people think sp stands for "stored procedure" when in fact it means "special." Stored procedures (as well as tables and views) stored in master with an sp_ prefix are accessible from any database without a proper reference (assuming a local version does not exist). If the procedure is marked as a system object (using sp_MS_marksystemobject (an undocumented and unsupported system procedure that sets is_ms_shipped to 1), then the procedure in master will execute in the context of the calling database.

您的代码中有几个错误: 你应该写成:

cmd.CommandText = "spCheckIfNameExist";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = name;

首先你需要告诉ADO引擎你正在调用一个存储过程而不是一个简单的命令文本,但是你还需要使用Add而不是AddWithValue来准确地确定传递给SP的参数类型.您的代码创建一个参数 int 因为 AddWithValue 的第二个参数是参数的值而不是类型。