PostgreSQL上的EF核心批量删除

时间:2019-01-27 23:33:32

标签: postgresql entity-framework-core

我正在尝试对单个表进行大规模的删除操作。 (在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。

这导致了几个问题:

  1. 无论如何,是否有EF Core可以使用LINQ等在Postgres上更有效地删除这些行? (在我看来,就像将上下文传递给一个可查询的对象,应在此处提供做出正确决定所需的一切)
  2. 如果没有,您对批量删除与仅将谓词交给数据库有何看法?

2 个答案:

答案 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运行这些函数并传递参数。