【问题标题】:Linq to SQL - Update BatchLinq to SQL - 更新批次
【发布时间】:2014-08-05 17:00:20
【问题描述】:

我有以下内联 SQL:

    internal void UpdateWorkflowProcessingByNullToken(Guid processingToken, int numberToProcess)
    {
        string sql = string.Format(CultureInfo.InvariantCulture,
            "UPDATE TOP ({0}) Master.WorkflowEventProcessing " +
            "SET ProcessingToken = '{1}' " +
            "WHERE ProcessingToken IS NULL",
            numberToProcess, processingToken);

        this.Database.ExecuteCommand(sql);
    }

内联 SQL 用于提高性能。我的理解是 L2S 会为我需要更新的每一行创建一个 SQL 语句。这花了太长时间。请注意,这是几年前的事了。

现在我有一个 DBA 告诉我:

  • 此查询似乎是最常被阻止或被阻止的查询之一。
  • 从数据库的角度来看,这没有优化,因为每次执行都需要生成执行计划。
  • 根据数据,这个简单的查询使用了超过 1GB 的计划缓存(超过 25000 个类似执行计划的副本),而如果只存储 1 个副本,它实际上只能使用不到 50KB 的内存。
  • 我建议创建一个以唯一标识符作为参数的存储过程。按照设计,存储过程(绕过编译阶段)应该比即席查询运行得更快。

作为开发人员,我们一直不愿意使用存储过程。我们喜欢在 C# 数据层中包含所有数据代码。我被困在这里了吗?我需要使用存储过程吗?或者有没有办法使用 L2S 进行大规模更新?

我记得读过有关编译 L2S 查询的文章。我也可以研究一下...

【问题讨论】:

  • 存储过程绝对没有问题,尤其是在正确使用的情况下。它们还允许您更新 SQL 而无需重新部署您的应用程序。
  • 我同意,但是创建存储过程对我们来说开销更大。这不是关于哪种方式更好。我们决定使用 L2S,如果可能的话,我们希望将它用于所有事情。如果很难绕过这个限制,那么我们将使用存储过程。我想至少先尝试一下。
  • 聪明的开发者喜欢存储过程——它工作得更快、更安全,并且在应用层和数据库层之间创建了一个清晰的 API。
  • 也就是说,numberToProcess 变化很大吗?如果您将此常量设为常量,那么您将不会有多个执行计划。
  • 另一个选项是将 ProcessingToken 移动到连接表中。然后,在更新令牌时,您不会阻止对 WorkflowEventProcessing 的读取。

标签: c# sql-server linq-to-sql


【解决方案1】:

您可以使用参数化的 SQL 命令来执行。这将生成一个可重用的查询执行计划,该计划在最初创建和缓存后将与存储过程一样高效。每次执行您只需提供新参数。

更多详情

鉴于以下代码更新了一个演示数据库和一个名为“Foo”的表

///////////////////////////////////////////////////////////
// just setup for the context for demo purposes, you would
// reference this.Database in place of creating context.
SqlConnection connection = new SqlConnection("Data Source = .; Initial Catalog = MyDb; Integrated Security = SSPI;");
var dataContext = new System.Data.Linq.DataContext(connection);
///////////////////////////////////////////////////////////

string updateQuery = "UPDATE TOP (@p1) dbo.Foo " +
"SET Data = @p2 " +
"WHERE Data IS NULL";

dataContext.Connection.Open();

var command = dataContext.Connection.CreateCommand();
command.CommandText = updateQuery;
command.CommandType = System.Data.CommandType.Text;

var param1 = new SqlParameter("@p1", System.Data.SqlDbType.Int);   
param1.Value = 3;
command.Parameters.Add(param1);

var param2 = new SqlParameter("@p2", System.Data.SqlDbType.Int);
param2.Value = 1;
command.Parameters.Add(param2);
command.Prepare();
command.ExecuteNonQuery();

param2.Value = 5;
command.ExecuteNonQuery();

从探查器输出中您可以看到它调用了 sp_prepexec

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@p1 int,@p2 int',N'UPDATE TOP (@p1) dbo.Foo SET Data = @p2 WHERE Data IS NULL',@p1=3,@p2=1
select @p1

并执行传递参数 3 和 1 的语句,然后当 param2.Value 设置为 5 并且再次执行命令时,分析器显示它重用了准备好的命令(因此没有重新编译或生成新的执行计划)

exec sp_execute 1,@p1=3,@p2=5

这就是分析器输出的样子,仅供参考...

【讨论】:

  • @Hogan,对,但 OP 并没有特别要求它是 L2S,但表示查询计划没有被重用的问题。这就是我关注的部分。
猜你喜欢
  • 1970-01-01
  • 2012-05-20
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
相关资源
最近更新 更多