Inefficient SQL generated for Bulk Update from in-memory collection
See original GitHub issueHi,
I am attempting to design a generic ‘bulk update’ method which uses a temp table to store the externally updated rows and joins the temp table with the actual table to do the update operation efficiently:
public void UpdateRange<T>(IEnumerable<T> source) where T : class, IEntity
{
using var connection = _context.CreateLinqToDbConnection();
connection.BeginTransaction();
using var tempTable = connection.CreateTempTable(source, tableName: $"{typeof(T).Name}UpdateTemp");
var entityTable = connection.GetTable<T>();
var updates = from e in entityTable
join te in tempTable on e.Id equals te.Id into gj
from te in gj
select te;
updates.Update(entityTable, e => e);
connection.CommitTransaction();
}
I use this strategy as opposed to a MERGE
statement to support RDBMS that don’t support MERGE
.
When working with the following test model and interfaces:
public interface IEntity
{
object Id { get; set; }
}
public interface IEntity<T> : IEntity
{
new T Id { get; set; }
}
public abstract class Entity<T> : IEntity<T>
{
public T Id { get; set; }
object IEntity.Id
{
get => Id;
set => Id = value != null ? (T) value : default;
}
}
public class Person : Entity<int>
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
The following SQL is generated:
UPDATE
[People]
SET
[Id] = (
SELECT
[gj].[Id]
FROM
[People] [e]
INNER JOIN [PersonUpdateTemp] [gj] ON [e].[Id] = [gj].[Id]
WHERE
[People].[Id] = [e].[Id]
),
[LastName] = (
SELECT
[gj_1].[LastName]
FROM
[People] [e_1]
INNER JOIN [PersonUpdateTemp] [gj_1] ON [e_1].[Id] = [gj_1].[Id]
WHERE
[People].[Id] = [e_1].[Id]
),
[FirstName] = (
SELECT
[gj_2].[FirstName]
FROM
[People] [e_2]
INNER JOIN [PersonUpdateTemp] [gj_2] ON [e_2].[Id] = [gj_2].[Id]
WHERE
[People].[Id] = [e_2].[Id]
)
WHERE
EXISTS(
SELECT
*
FROM
[People] [e_3]
INNER JOIN [PersonUpdateTemp] [gj_3] ON [e_3].[Id] = [gj_3].[Id]
WHERE
[People].[Id] = [e_3].[Id]
)
I was hoping to have a single join and update statement updating each column from the join result. How would I accomplish this?
I have a repo that contains a test that produces the above SQL here (see the Linq2DbUpdateTests
class). I retrieved the SQL by setting a breakpoint in the TestService
class on the last line of the method connection.CommitTransaction();
and inspecting the Command property’s SQLText property.
Environment details
linq2db version: 3.2.3 Database Server: Sqlite Database Provider: EF Core (via Linq2db.EntityFrameworkCore Operating system: Windows 10 .NET Framework: .NET Core
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (4 by maintainers)
I have written this UPDATE for SQLite approx 4 years ago. So it’s time to review this part.
Actually, we will try to fix it for next release too