我正在尝试对单个表进行大规模的删除操作。 (在1m行表上考虑100,000行)
我正在使用PostgreSQL和EntityFrameworkCore。
详细信息:应用程序代码具有要匹配的谓词,并且不知道有多少行可能与该谓词匹配。可能是0行/秒或非常大的数目。
研究表明EF Core无法有效处理此问题。 (即以下代码为每一行生成一个Delete语句!)
Using (var db = new DbContext)
var queryable = db.Table.AsQueryable()
.Where(o => o.ForeignKey == fKey)
.Where(o => o.OtherColumn == false);
db.Table.RemoveRange(queryable);
await db.SaveChangesAsync();
这是我希望以某种批处理操作运行的SQL:
delete from Table
where ForeignKey = 1234
and OtherColumn = false
and PK in (
select PK
from Table
where ForeignKey = 1234
and OtherColumn = false
limit 500
)
那里有扩展库,但是我还没有找到一个支持Postgres的活跃库。我目前正在通过EF Core执行上面的原始sql。
这导致了几个问题:
答案 0 :(得分:0)
免责声明:我是项目doc
的所有者您的情况看起来像是我们的Batch Delete
功能可以处理的:Entity Framework Plus
Using (var db = new DbContext)
var queryable = db.Table.AsQueryable()
.Where(o => o.ForeignKey == fKey)
.Where(o => o.OtherColumn == false);
queryable.Delete();
实体未加载到应用程序中,仅按照您指定的方式执行SQL。
答案 1 :(得分:0)
我认为您正在尝试做一些您不应该使用EntityFrameworkCore的事情。 EntityFrameworkCore的对象是一种在.Net-Core应用程序和数据库之间移动数据的好方法。典型的用途是单个或少量物体。对于批量操作,有一些nuget软件包。有this包用于通过postgres进行插入和更新。This article by the creator说明了它如何使用临时表和postgres COPY命令来进行批量操作。这向我们展示了一种通过ID批量删除行的方法:
var toDelete = GetIdsToDelete();
using (var conn = new NpgsqlConnection(connectionString))
{
conn.Open();
using ( var cmd = conn.CreateCommand())
{
cmd.CommandText =("CREATE TEMP TABLE temp_ids_to_delete (id int NOT NULL) ON COMMIT DROP ");
cmd.Prepare();
cmd.ExecuteNonQuery();
}
using (var writer = conn.BeginBinaryImport($"COPY temp_ids_to_delete (id) FROM STDIN (FORMAT BINARY)"))
{
foreach (var id in toDelete)
{
writer .StartRow();
writer .Write(id);
}
writer .Complete();
}
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "delete from myTable where id in(select id from temp_ids_to_delete)";
cmd.Prepare();
cmd.ExecuteNonQuery();
}
conn.Close();
通过一些小的更改,可以将其概括化。
但是您想做些不同的事情。您不想在应用程序和数据库之间移动数据或信息。您想使用efcore即时创建一个slq-procedure并在服务器上运行它。问题在于ef core并不是真正做到这一点的工具。但是也许有解决方法。我可以想到的一种方法是使用ef-core来构建查询,获取查询字符串,然后将该字符串插入另一个在服务器上运行的sql字符串中。 当前获取查询字符串并不容易,但显然要使用EF Core 5.0。然后,您可以这样做:
var queryable = db.Table.AsQueryable()
.Where(o => o.ForeignKey == fKey)
.Where(o => o.OtherColumn == false);
var queryString=queryable.ToQueryString();
db.Database.ExecuteSqlRaw("delete from Table where PK in("+queryString+")" )
是的,这确实很hacky,我不建议这样做。我建议在databaseServer上编写过程和函数,因为这不是ef-core应该使用的东西。然后,您仍然可以从ef-core运行这些函数并传递参数。