从数据库端的组中获取最后 2 行
Get last 2 rows from group on database side
我正在尝试提高 Postgre 的 linq 查询性能SQL。有两个表(Parcles、ParcelStates)具有关系 1:n。我需要为每个包裹获取最后 2 个 ParcelStates。看起来很简单,我有以下代码:
IQueryable<Parcel> parcels = _dbContext.Parcels
.OrderByDescending(x => x.Id)
.Take(100);
然后获取状态:
var states = await parcels
.GroupJoin(_dbContext.ParcelStates, ps => ps.Id, p => p.ParcelId, (ps, p) => new { ps, p })
.SelectMany(x => x.p.DefaultIfEmpty().OrderByDescending(y => y.Id).Take(2), (x,c) => c)
.ToListAsync();
它returns我180状态,还可以。但是存在性能问题,因为它生成不执行 SQL 查询:
SELECT *
FROM (
SELECT *
FROM parcels AS x
WHERE x.isdeleted = FALSE
ORDER BY c DESC, c0 DESC
LIMIT @__p_1 OFFSET @__p_0
) AS t
LEFT JOIN parcelstates AS p ON t.id = p.parcelid
ORDER BY t.c DESC, t.c0 DESC, t.id
它从数据库中获取所有状态,而我只需要 2 个。
如何更改 LINQ 以在数据库端过滤结果?
我在日志中发现:
LINQ 表达式 'Take(2)' 无法翻译,将被计算
我们可以使用一个 foreach 循环,它将转化为几个非常快速的 SQL 查找(应该在 < 1 秒内执行)。不理想,但我仍然建议编写存储过程来获取此数据,而不是依赖 LINQ to SQL,它并不总是生成最佳查询:
// Store a list of parcel states
var parcelStates = new List<ParcelState>();
// Read top 100 parcels from the database
var parcels = dbContext.Parcels
.OrderBy(p => p.Id)
.Take(100);
// For each parcel, use SQL to lookup the 2 most recent parcel states
foreach (var p in parcels)
{
var ps = dbContext.ParcelStates
.Where(ps => ps.ParcelId == p.Id)
.OrderByDescending(ps => ps.Id)
.Take(2);
parcelStates.AddRange(ps);
}
// Now we have all parcel states for those parcels
Console.WriteLine($"Found {parcelStates.Count} parcel states for {parcels.Count} parcels");
如果将 SelectMany
表达式插入 GroupJoin
,它会转换为 SQL 吗?
var states = await parcels
.GroupJoin(_dbContext.ParcelStates, ps => ps.Id, p => p.ParcelId,
(ps, p) => p.DefaultIfEmpty().OrderByDescending(y => y.Id).Take(2))
.ToListAsync();
我正在尝试提高 Postgre 的 linq 查询性能SQL。有两个表(Parcles、ParcelStates)具有关系 1:n。我需要为每个包裹获取最后 2 个 ParcelStates。看起来很简单,我有以下代码:
IQueryable<Parcel> parcels = _dbContext.Parcels
.OrderByDescending(x => x.Id)
.Take(100);
然后获取状态:
var states = await parcels
.GroupJoin(_dbContext.ParcelStates, ps => ps.Id, p => p.ParcelId, (ps, p) => new { ps, p })
.SelectMany(x => x.p.DefaultIfEmpty().OrderByDescending(y => y.Id).Take(2), (x,c) => c)
.ToListAsync();
它returns我180状态,还可以。但是存在性能问题,因为它生成不执行 SQL 查询:
SELECT *
FROM (
SELECT *
FROM parcels AS x
WHERE x.isdeleted = FALSE
ORDER BY c DESC, c0 DESC
LIMIT @__p_1 OFFSET @__p_0
) AS t
LEFT JOIN parcelstates AS p ON t.id = p.parcelid
ORDER BY t.c DESC, t.c0 DESC, t.id
它从数据库中获取所有状态,而我只需要 2 个。
如何更改 LINQ 以在数据库端过滤结果?
我在日志中发现: LINQ 表达式 'Take(2)' 无法翻译,将被计算
我们可以使用一个 foreach 循环,它将转化为几个非常快速的 SQL 查找(应该在 < 1 秒内执行)。不理想,但我仍然建议编写存储过程来获取此数据,而不是依赖 LINQ to SQL,它并不总是生成最佳查询:
// Store a list of parcel states
var parcelStates = new List<ParcelState>();
// Read top 100 parcels from the database
var parcels = dbContext.Parcels
.OrderBy(p => p.Id)
.Take(100);
// For each parcel, use SQL to lookup the 2 most recent parcel states
foreach (var p in parcels)
{
var ps = dbContext.ParcelStates
.Where(ps => ps.ParcelId == p.Id)
.OrderByDescending(ps => ps.Id)
.Take(2);
parcelStates.AddRange(ps);
}
// Now we have all parcel states for those parcels
Console.WriteLine($"Found {parcelStates.Count} parcel states for {parcels.Count} parcels");
如果将 SelectMany
表达式插入 GroupJoin
,它会转换为 SQL 吗?
var states = await parcels
.GroupJoin(_dbContext.ParcelStates, ps => ps.Id, p => p.ParcelId,
(ps, p) => p.DefaultIfEmpty().OrderByDescending(y => y.Id).Take(2))
.ToListAsync();