簡體   English   中英

PostgreSQL 上的 EF Core 批量刪除

[英]EF Core Bulk Delete on PostgreSQL

我正在嘗試對單個表執行潛在的大規模刪除操作。 (想想 1m 行表上的 100,000 行)

我正在使用 PostgreSQL 和 EntityFrameworkCore。

詳細信息:應用程序代碼有一個要匹配的謂詞,並且不知道有多少行可能與該謂詞匹配。 它可能是 0 row/s 或非常大的數量。

研究表明 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. 如果沒有,您對批量刪除與僅將數據庫處理為謂詞有何看法?

免責聲明:我是Entity Framework Plus項目的所有者

您的場景看起來是我們的Batch Delete功能可以處理的: https : //entityframework-plus.net/batch-delete

Using (var db = new DbContext)
 var queryable = db.Table.AsQueryable()
       .Where(o => o.ForeignKey == fKey)
       .Where(o => o.OtherColumn == false);

queryable.Delete();

實體未加載到應用程序中,僅按您指定的方式執行 SQL。

我認為您正在嘗試做一些不應該使用 EntityFrameworkCore 的事情。 EntityFrameworkCore 的目標是提供一種在 .Net-Core 應用程序和數據庫之間移動數據的好方法。 典型的使用方式是單個或少量對象。 對於批量操作,有一些 nuget 包。 這個用於插入和更新 postgres 的包。 作者的這篇文章解釋了它如何使用臨時表和 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 過程並在服務器上運行該過程。 問題是 ef 核心並不是真正的構建來做到這一點。 但也許有辦法解決這個問題。 我能想到的一種方法是使用 ef-core 構建查詢,獲取查詢字符串,然后將該字符串插入另一個 sql-string 以在服務器上運行。 獲取查詢字符串目前並不容易,但顯然它會與 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 運行這些函數並傳遞參數。

我建議使用臨時表來執行這樣的操作。 您將創建一個鏡像臨時表,將要保留或刪除的記錄批量添加到臨時表中,然后執行刪除操作以在該臨時表中/不在該臨時表中查找記錄。 嘗試使用諸如 PgPartner 之類的庫來輕松完成批量添加和臨時表創建。

查看 PgPartner: https ://www.nuget.org/packages/PgPartner/

https://github.com/SourceKor/PgPartner

這可以通過BulkExtensions完成

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM