【问题标题】:Pros and Cons of using SqlCommand Prepare in C#?在 C# 中使用 SqlCommand Prepare 的优缺点?
【发布时间】:2011-01-27 20:02:06
【问题描述】:

当我阅读书籍学习 C#(可能是一些旧的 Visual Studio 2005 书籍)时,我遇到过建议每次执行 SQL 调用时始终使用 SqlCommand.Prepare(无论是 SELECT/UPDATE 还是INSERT 在 SQL SERVER 2005/2008 上),我将参数传递给它。 但真的是这样吗?

  1. 应该每次都做吗?还是只是有时?

  2. 传递一个参数、五个或二十个参数是否重要?

  3. 如果有的话,它应该给予什么提升?会不会很明显(我一直在这里使用SqlCommand.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.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 如果它是字符串等?

【问题讨论】:

    标签: c# sql-server performance


    【解决方案1】:

    根据IDbCommand.Prepare Method documentation

    服务器会根据需要自动缓存计划以供重用; 因此,无需直接在您的 客户端应用程序。

    我还发现了这个Databases Administrators answer,它为您提供了很多关于prepare 方法的详细信息,以及为什么我没有得到任何显着改进。

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

    【讨论】:

      【解决方案2】:

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

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

      换句话说:如果您执行许多 SQL 命令(它们完全相同或仅在参数值上有所不同),您将看到巨大的性能提升。

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

      【讨论】:

      • 我绝对相信你在说什么 - 但@OP:请考虑相反的情况。根据准备好的语句的数据库可能会给查询分析器带来困难并选择错误的索引。我们在 DB2 上看到了这一点,其中列的值分布非常不均匀。就像每个性能建议一样:您必须针对您的场景进行衡量
      【解决方案3】:

      来自 MSDN 文档:

      "在调用 Prepare 之前,请指定 中每个参数的数据类型 要准备的声明。对于每个 具有可变长度的参数 数据类型,必须设置 Size 属性到所需的最大大小。 如果这些,Prepare 会返回错误 条件不满足。

      如果您在之后调用 Execute 方法 调用 Prepare,任何参数值 大于值 Size 属性指定的是 自动截断为 原始指定的大小 参数,并且没有截断错误 被退回。

      输出参数(无论是准备好的还是 not) 必须有用户指定的数据 类型。如果您指定可变长度 数据类型,您还必须指定 最大尺寸。”

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

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

      我查看了它并查看了这篇文章http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.prepare.aspx。您的问题是您需要在运行 .Prepare() 之前定义参数,然后在运行 .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.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 注入吗?在提出这个问题之前,我自己已经阅读了 MSDN 文档,并且引用的部分对于我是否使用它以及它是否具有某些网站上似乎建议的任何性能优势并不是真正有用的。
      • 我的理解是通过使所有定义的内容都可以缓存和重用查询计划。我猜它不会给你带来极大的好处。
      • 我在代码中的用法是否正确?在设置了所有参数并引发错误后,我尝试将 Prepare 移至。那么是不是 sqlQuery.Prepare 应该总是在参数之前使用呢?
      • 部分好。您没有注意到链接 SqlParameter idParam = new SqlParameter("@id", SqlDbType.Int, 0); SqlParameter descParam = new SqlParameter("@desc", SqlDbType.Text, 100); idParam.Value = 20;这就是我猜的全部内容?但是哇,如果这是添加 4+ 新行以使用 Sql.Prepare() 的解决方案,那么没有它我可能会更好。
      • 肯定有更好的方法吗?
      【解决方案4】:

      另一个好处是,这样做可以编译、缓存和重用 SQL 查询计划。如果您的查询调用量很少,这没什么大不了的,但如果您有很多,这样做确实有一些显着的性能优势。

      【讨论】:

      • 你能添加任何支持链接吗?
      • 由于 SqlCommand 对象使用了 SqlParameters,该计划无论如何都会被重用,因为 .net 会将 sql 语句包装到将生成可重用计划的 sp_executesql 过程中。
      猜你喜欢
      • 2011-12-06
      • 1970-01-01
      • 1970-01-01
      • 2015-05-05
      • 1970-01-01
      • 2013-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多