C# 在C中使用SqlCommand Prepare的利弊?

C# 在C中使用SqlCommand Prepare的利弊?,c#,sql-server,performance,C#,Sql Server,Performance,当我阅读书籍学习C时,可能是一些旧的Visual Studio 2005书籍,我遇到了始终使用SqlCommand的建议。每次执行SQL调用时,无论是“SQL SERVER 2005/2008上的选择/更新还是插入”,都要做好准备,并将参数传递给它。但真的是这样吗 是否应该每次都这样做?或者只是偶尔 传递的是一个参数还是五个或二十个参数重要吗 如果有的话,它应该给予什么提振?我一直在使用SqlCommand,这里准备,那里跳过,从来没有遇到过任何问题或明显的差异 为了回答这个问题,这是我通常使用

当我阅读书籍学习C时,可能是一些旧的Visual Studio 2005书籍,我遇到了始终使用SqlCommand的建议。每次执行SQL调用时,无论是“SQL SERVER 2005/2008上的选择/更新还是插入”,都要做好准备,并将参数传递给它。但真的是这样吗

是否应该每次都这样做?或者只是偶尔

传递的是一个参数还是五个或二十个参数重要吗

如果有的话,它应该给予什么提振?我一直在使用SqlCommand,这里准备,那里跳过,从来没有遇到过任何问题或明显的差异

为了回答这个问题,这是我通常使用的代码,但这是一个更一般的问题

public static decimal pobierzBenchmarkKolejny(string varPortfelID, DateTime data, decimal varBenchmarkPoprzedni, decimal varStopaOdniesienia) {
    const string preparedCommand = @"SELECT [dbo].[ufn_BenchmarkKolejny](@varPortfelID, @data, @varBenchmarkPoprzedni,  @varStopaOdniesienia) AS 'Benchmark'";
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP)) //if (varConnection != null) {
    using (var sqlQuery = new SqlCommand(preparedCommand, varConnection)) {
        sqlQuery.Prepare();
        sqlQuery.Parameters.AddWithValue("@varPortfelID", varPortfelID);
        sqlQuery.Parameters.AddWithValue("@varStopaOdniesienia", varStopaOdniesienia);
        sqlQuery.Parameters.AddWithValue("@data", data);
        sqlQuery.Parameters.AddWithValue("@varBenchmarkPoprzedni", varBenchmarkPoprzedni);
        using (var sqlQueryResult = sqlQuery.ExecuteReader())
            if (sqlQueryResult != null) {
                while (sqlQueryResult.Read()) {

                }
            }
    }
}
补充澄清:

如果我移动sqlQuery.Prepare,就像下面的代码一样,会抛出一个异常,即必须显式声明大小,这基本上会让我认为,先使用sqlQuery.Prepare会使它毫无用处?有人能用我的例子说明正确的用法吗

public static decimal pobierzBenchmarkKolejny(string varPortfelID, DateTime data, decimal varBenchmarkPoprzedni, decimal varStopaOdniesienia) {
    const string preparedCommand = @"SELECT [dbo].[ufn_BenchmarkKolejny](@varPortfelID, @data, @varBenchmarkPoprzedni,  @varStopaOdniesienia) AS 'Benchmark'";
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP)) //if (varConnection != null) {
    using (var sqlQuery = new SqlCommand(preparedCommand, varConnection)) {

        sqlQuery.Parameters.AddWithValue("@varPortfelID", varPortfelID);
        sqlQuery.Parameters.AddWithValue("@varStopaOdniesienia", varStopaOdniesienia);
        sqlQuery.Parameters.AddWithValue("@data", data);
        sqlQuery.Parameters.AddWithValue("@varBenchmarkPoprzedni", varBenchmarkPoprzedni);
        sqlQuery.Prepare();
        using (var sqlQueryResult = sqlQuery.ExecuteReader())
            if (sqlQueryResult != null) {
                while (sqlQueryResult.Read()) {

                }
            }
    }
}

我该怎么做?在参数旁边添加.size,如果是字符串,则执行varPortfel.Lenght,等等

来自MSDN文档:

在调用Prepare之前,请指定 中每个参数的数据类型 待编写的声明。每人 具有可变长度的参数 数据类型,则必须设置大小 属性设置为所需的最大大小。 如果出现以下情况,Prepare将返回一个错误 不符合条件

如果在之后调用Execute方法 调用Prepare,任何参数值 这比值大 由Size属性指定的是 自动截断为 原始指定尺寸的 参数,并且没有截断错误 他们回来了

输出参数,无论是否准备好 不必须具有用户指定的数据 类型如果指定可变长度 数据类型,还必须指定 最大尺寸

此外,如果CommandType 属性设置为TableDirect, 准备什么都不做。If命令类型 设置为StoredProcedure,则调用 准备应该成功

这通常用于确保最终用户没有使用SQL注入技术在数据库中添加或删除您不希望的信息

我调查了一下,并查看了这篇文章。您的问题是您需要在运行前定义参数。准备,然后在运行后设置参数。准备。现在你以前两个都做过。我会尝试类似于这个注释的东西,因为我没有测试它,所以我的语法可能有点不正确

public static decimal pobierzBenchmarkKolejny(string varPortfelID, DateTime data, decimal varBenchmarkPoprzedni, decimal varStopaOdniesienia) {
    const string preparedCommand = @"SELECT [dbo].[ufn_BenchmarkKolejny](@varPortfelID, @data, @varBenchmarkPoprzedni,  @varStopaOdniesienia) AS 'Benchmark'";
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP)) //if (varConnection != null) {
    using (var sqlQuery = new SqlCommand(preparedCommand, varConnection)) {

        sqlQuery.Parameters.Add("@varPortfelID");
        sqlQuery.Parameters.Add("@varStopaOdniesienia");
        sqlQuery.Parameters.Add("@data");
        sqlQuery.Parameters.Add("@varBenchmarkPoprzedni");

        sqlQuery.Prepare();
        sqlQuery.ExecuteNonQuery();//This might need to be ExecuteReader()

        sqlQuery.Parameters[0].Value = varPortfelID;
        sqlQuery.Parameters[1].Value = varStopaOdniesienia;
        sqlQuery.Parameters[2].Value = data;
        sqlQuery.Parameters[3].Value = varBenchmarkPoprzedni;

        using (var sqlQueryResult = sqlQuery.ExecuteReader())
            if (sqlQueryResult != null) {
                while (sqlQueryResult.Read()) {

                }
            }
    }
}

另一个好处是,通过这样做,可以编译、缓存和重用SQL查询计划。如果对查询的调用量很小,那么这并不是什么大问题,但是如果您有很多,那么这样做确实有一些显著的性能优势。

根据我自己的经验:性能提升非常显著。不久前,我在一个项目中工作,我们使用了自己的对象关系映射。我们使用巨大的数据库作为复杂对象模型的持久存储—具有按需对象加载和弱引用对象生命周期

使用准备好的命令对于该应用程序的成功至关重要,因为只有它才能使系统真正可用

换句话说:如果您执行许多SQL命令——这些命令完全相同或只是参数值不同——您将看到巨大的性能提升

我没有确切的数字或链接,但我可以证明我自己的经验。

根据:

服务器根据需要自动缓存计划以供重用; 因此,不需要在应用程序中直接调用此方法 客户端应用程序

我还发现了这一点,它为您提供了很多关于prepare方法的细节,以及为什么我没有得到任何显著的改进


关于SQL注入,您将受到保护,因为您使用了参数化的sqlCommand。。。不是因为您调用了prepare方法

性能和其他方面呢?它只是为了防止SQL注入吗?在提出这个问题之前,我自己已经阅读了MSDN文档,引用的部分对于我的问题(是否使用它以及它是否具有某些网站上建议的任何性能优势)并没有真正的用处。我的理解是,通过定义所有内容,它可以缓存和重用查询计划。我猜它不会给你带来太多的好处。我在代码中的用法正确吗?我尝试在设置所有参数并抛出错误后将其移动到“准备”。那么sqlQuery.Prepare应该总是在参数之前使用,还是部分使用。您没有注意到链接SqlParameter idParam=new中的错误SqlParameter@id,SqlDbType.Int,0;SqlParameter descParam=newSqlParameter@desc,SqlDbType.Text,100;Iparam.值=20;这就是我想的全部?但是哇,如果这是一个添加4+行以使用Sql的解决方案
如果没有它可能会更好。肯定有更好的方法吗?你能添加任何支持链接吗?由于SqlCommand对象使用了SqlParameters,所以该计划仍然可以重用,因为.NET将把SQL语句封装到一个SPEXECUTESTESQL程序中,它将生成一个可重用的计划。我绝对相信你所说的话,但是@ OP:请考虑相反的情况。根据数据库是否准备好语句,查询分析器可能会遇到困难,并选择错误的索引。我们看到了这一点,尤其是在DB2上,列的值分布非常不均匀。正如每个性能建议一样:您必须针对您的场景进行度量