简体   繁体   中英

How do I delete records before a specified date in Entity Framework Core excluding the latest?

How can I delete all but the latest stock records, that were created before a specific date, in Entity Framework Core. I am unable to figure out the required LINQ query but have managed to put together SQL that should do the job:

--
-- Parameters. 
--
DECLARE @PurgeDate DATETIME = DATEADD(day, -7, GETDATE());
DECLARE @RegionId INT = 28;

DECLARE @StockCodes TABLE(
    StockCode NVARCHAR(10)
);

-- Could be a significant number
INSERT INTO @StockCodes VALUES ('ABC123'), ('DEF123') /* etc... */;

--
-- Get stock records that are newer than the purge date or the latest record if not. 
-- This ensures there is always at least one stock record for a stock code.
--
WITH LatestStockRecords
AS
(
    SELECT s.*, [RowNumber] = ROW_NUMBER() OVER (PARTITION BY s.[StockCode] ORDER BY s.[CreatedAt] DESC)
    FROM StockRecords AS s
        INNER JOIN Locations AS l
            ON s.[LocationId] = l.[Id]
    WHERE l.[RegionId] = @RegionId
        AND s.[StockCode] IN (SELECT * FROM @StockCodes)
)
SELECT *.[Id]
INTO #_STOCK_RECORD_IDS
FROM LatestStockRecords
WHERE [CreatedAt] >= @PurgeDate
    OR [RowNumber] = 1;
    
--
-- Delete the stock records that do not appear in the latest stock records temporary table.
--
DELETE s
FROM StockRecords AS s
    INNER JOIN Locations AS l
        ON s.[LocationId] = l.[Id]
WHERE l.[RegionId] = @RegionId
    AND s.[StockCode] IN (SELECT * FROM @StockCodes)
    AND s.[Id] NOT IN (SELECT * FROM #_STOCK_RECORD_IDS);

There could be a significant number of records to delete so performance needs to be a consideration.

EDIT: Removed DbContext and entities as I don't think they're relevant to the question.

This is how I eventually solved the problem. I had to force the evaluation of the grouping query as Entity Framework Core doesn't seem to support the neccessary query at this point.

var regionId = 28;
var stockCodes = new string[] { "ABC123", "DEF123" /* etc... */ };
var purgeDate = DateTime.UtcNow.AddDays(-NumberOfDaysToPurge);

bool IsPurgeable(StockRecord stockRecord)
{
    return stockRecord.CreatedAt >= purgeDate;
}

var latestStockRecordIds = context.StockRecords
    .Where(stockRecord =>
        stockRecord.Location.RegionId == regionId
        && stockCodes.Contains(stockRecord.StockCode))
    .AsEnumerable() // <-- force execution
    .GroupBy(stockRecord => stockRecord.StockCode)
    .SelectMany(group =>
    {
        var orderedStockRecords = group.OrderByDescending(stockRecord => stockRecord.CreatedAt);

        var stockRecords = orderedStockRecords.Count(IsPurgeable) > 0
            ? orderedStockRecords.Where(IsPurgeable)
            : orderedStockRecords.Take(1);

        return stockRecords.Select(stockRecord => stockRecord.Id);
    });

var stockRecordsToRemove = await context.StockRecords
    .Where(stockRecord =>
        stockRecord.Location.RegionId == regionId
        && StockCodeCodes.Contains(stockRecord.StockCode)
        && stockRecord.CreatedAt <= purgeDate
        && !latestStockRecordIds.Contains(stockRecord.Id))
    .ToListAsync();

context.ChangeTracker.AutoDetectChangesEnabled = false;
context.StockRecords.RemoveRange(stockRecordsToRemove);

await context.SaveChangesAsync();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM