简体   繁体   中英

Linq to SQL - Update Batch

I have the following inline 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);
    }

Inline SQL was used for performance. It was my understanding that L2S would create a SQL statement for every row that I needed to update. And that was taking too long. Note, this was a couple of years ago.

Now I have a DBA telling me this:

  • This query appears to be one of the most frequently blocking or been blocked.
  • This is not optimized, from database perspective, due to execution plan need to be generated for every execution.
  • Based on the data, this simple query is using more than 1GB of plan cache (more than 25000 copies of similar execution plans), while it could actually use only less than 50KB of memory, if only 1 copy is stored.
  • I would propose to create a stored procedure with the unique identifier as parameter. By design, the stored procedure (bypass compilation stage) should run faster than ad hoc query.

As developers, we've been reluctant to use stored procedures. We like having all of our data code in our C# data layer. Am I stuck here? Do I need to use a stored procedure? Or is there a way to do a mass update with L2S?

I remember reading about compiling an L2S query. I could look into that as well...

You can use parameterized SQL commands to execute. This will generate a reusable query execution plan that will be as efficient as a stored procedure after it is initially created and cached. Each execution you simply supply new parameters.

More Details

Given the following code that updates a demo database and a table named "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();

From the profiler output you can see it calls 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

and executes the statement passing the parameters 3 and 1 then when param2.Value is set to 5 and the command executed again the profiler shows it reusing the prepared command (thus no recompiling or new execution plan generated)

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

This is what the profiler output looks like, FYI... 在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM