【问题标题】:LinqToSql DeleteAllOnSubmit becomes slow from a foreach loopLinq To Sql Delete OnSubmit 从 foreach 循环变慢
【发布时间】:2020-12-02 00:10:04
【问题描述】:

想做一些 SQL 处理,并通过在我的项目中添加 dbml 来尝试 LinqToSql 功能。 它是一个 foreach 循环(大约 3k 个条目),我打算从中执行一个简单的 where 子句并在 2 个表上执行后续 deleteallonsubmit。 (分别记录 23 万和 540 万)。

根据我在 Spark 延迟计算中工作的理解和假设。在我调用 SubmitChanges() 之前,我认为它不会在 foreach 的每次迭代中或者更简单的说法中进行数据库调用并在实际表上执行“某事”。我从 SQL 分析器中注意到,它确实在每次迭代中都进入了 SQL 服务器,并且每次迭代大约需要 1 秒

下面是代码

  internal static void RemoveAllDataStartingFrom()
  {
     HistoricalRefreshDateCheckerDataContext dbContext = new HistoricalRefreshDateCheckerDataContext();
     logger = (ILog)LogManager.GetLogger("HDP.allMins");
     var counter = 0;
     allMins.ToList().ForEach((v) =>
     {
        logger.Info("For each started");
        var toBeDeletedDaily = dbContext.DailyIntervalDatas.Where(x => x.Date >= v.Value && x.Id == v.Key);
        logger.Info("Daily where completed");

        dbContext.DailyIntervalDatas.DeleteAllOnSubmit(toBeDeletedDaily);
        logger.Info("Daily deleteall completed");

        var toBeDeletedFifteenMinute = dbContext.FifteenMinuteDatas.Where(x => x.Date >= v.Value && x.Id == v.Key);
        logger.Info("Fifteen Where completed");

        dbContext.FifteenMinuteDatas.DeleteAllOnSubmit(toBeDeletedFifteenMinute);
        logger.Info("Fifteen deleteall completed ");

        logger.Info("ended. counter = " + counter++);

     });
     dbContext.SubmitChanges();
  }

我的意图

我想将整个事情作为一个批处理命令运行(通过进行 1 或 2 次数据库调用)。我本可以编写一个动态内联脚本并在 DB 上触发它。但我想我错误地使用了 LinqToSql 功能。

以下是我的日志文件

编辑

allMins 代码

 internal static void FetchIndividualStartDates(DateTime startDate)
  {

     HistoricalRefreshDateCheckerDataContext refDBData = new HistoricalRefreshDateCheckerDataContext();
     var allMaxes = refDBData.DailyIntervalDatas
           .GroupBy(x => x.Id)
           .Select(
           row =>
              new
              {
                 row.Key,
                 MaxDate = row.Max(r => r.Date)
              });
     allMaxes.ToList().ForEach((a) =>
     {         
        allMins[a.Key] = Convert.ToDateTime(a.MaxDate);
     });
  }

其他 Profiler 日志

在下面的每个 DeleteAllOnSubmit 被调用

exec sp_executesql N'SELECT [t0].[x], [t0].[y], [t0].[z], [t0].[a], [t0].[b], [t0].[c], [t0].[e], [t0].[Date], [t0].[Id], [t0].[_ID] AS [_ID]
FROM [dbo].[FifteenMinuteData] AS [t0]
WHERE ([t0].[Date] >= @p0) AND ([t0].[Id] = @p1)',N'@p0 datetime,@p1 varchar(8000)',@p0='2020-08-10 00:00:00',@p1='1365'

当 SubmitChanges() 被调用时,他会被触发

出于数据安全目的,我编辑了一些信息

【问题讨论】:

  • 什么是allMins,它来自哪里?
  • @GertArnold : allMins 是一个键值对字典。目前有 3k+ 个条目。我通过一些琐碎的查询来准备它。但是,是的,我正在循环它。
  • 您可能应该将该查询与您在此处显示的查询集成。
  • @GertArnold :添加代码。
  • any 一样,ORM(也是实体框架)对象被拉入内存,修改或标记为删除,然后单独保存回数据库。这正是每次迭代中发生的事情。没有办法改变这种根深蒂固的行为。这就是为什么您不应该将 ORM 用于批量数据库操作的原因。这些第三方库只是治标不治本,都有自己的问题。我的座右铭:做对或不做。 IE。存储过程。这就是我要说的全部内容。

标签: sql linq linq-to-sql


【解决方案1】:

您的日志不会指出实际 SQL 执行发生的位置,只是指定的行正在执行。对于 SQL 执行,您需要执行 dbContext.Log = Console.Out 之类的操作。

使用 Linq to SQL,带有 SubmitChanges 的工作单元模式会等待语句排队,但会针对每条记录向数据库发出单独的查询。它不会将语句批处理到单个数据库请求中。为此,您将需要第三方扩展或升级到支持批处理的 EF Core 之类的东西。

更好的是,对于批量操作,您可能需要考虑使用存储过程或自定义 SQL 进行删除,使用 SqlBulkCopy 进行插入。 LINQ 非常适合加载对象、操作内存中的对象并将它们保存回来,但不适用于像 DELETE FROM table WHERE parentId = @ParentId 这样的批量操作。

编辑:根据附加信息,您在调用 DeleteAllOnSubmit 时看不到删除语句,而是在将项目列表添加到被删除。与其循环遍历 allmins,不如考虑如何将 allMins 与 delete 操作结合起来,并可能删除 forEach 迭代以将选择包装成单遍。考虑如何在 SQL 中本地创建一个包含 where/join 子句中的 All Mins 逻辑的单个 DELETE FROM 语句,这可能有助于指导您解决这部分问题。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多