You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core中Group By后获取分组键及Decimal列表并计算中位数

EF Core分组后计算中位数实现方案

需求场景

现有EF Core分组代码可按城市名称(含阿拉伯语名称)分组,获取分组键及AmountSqft的Decimal列表,需保留分组键和该列表的同时,计算列表的中位数(替代Linq内置的Average方法)。

原代码

var response = await _db.Transaction
                        .Where(expression)
                        .OrderBy(x => x.AmountSqft)
                        .GroupBy(x => new { CityName = x.City.Name, CityNameAr = x.City.NameAr })
                        .Select(x => new SalesTrendDTO
                                         {
                                             Label = x.Key.CityName,
                                             LabelAr = x.Key.CityNameAr,
                                             ListAvgPricePerSqft = x.Select(z => z.AmountSqft).ToList()
                                         }).ToListAsync();

实现方案

由于EF Core无法将自定义中位数逻辑转换为SQL语句,通用做法是先将分组后的列表数据拉取到内存,再在内存中计算中位数,具体步骤如下:

1. 调整数据查询逻辑

先从数据库获取分组后的基础数据(分组键+数值列表),再转换为目标DTO并计算中位数:

// 从数据库拉取分组后的基础数据
var rawData = await _db.Transaction
                        .Where(expression)
                        .GroupBy(x => new { x.City.Name, x.City.NameAr })
                        .Select(x => new 
                        {
                            CityName = x.Key.Name,
                            CityNameAr = x.Key.NameAr,
                            AmountSqftList = x.Select(z => z.AmountSqft).ToList()
                        })
                        .ToListAsync();

// 转换为带中位数的目标DTO
var result = rawData.Select(item => new SalesTrendDTO
{
    Label = item.CityName,
    LabelAr = item.CityNameAr,
    ListAvgPricePerSqft = item.AmountSqftList,
    MedianPricePerSqft = CalculateMedian(item.AmountSqftList)
}).ToList();

2. 实现中位数计算方法

添加一个通用的中位数计算函数,处理奇偶长度的列表:

private decimal CalculateMedian(List<decimal> values)
{
    if (values == null || values.Count == 0)
        throw new ArgumentException("数值列表不能为空");

    // 先对列表排序
    var sortedValues = values.OrderBy(v => v).ToList();
    int count = sortedValues.Count;

    if (count % 2 == 1)
    {
        // 奇数个元素,取中间位置的数值
        return sortedValues[count / 2];
    }
    else
    {
        // 偶数个元素,取中间两个数值的平均值
        var middleLeft = sortedValues[(count / 2) - 1];
        var middleRight = sortedValues[count / 2];
        return (middleLeft + middleRight) / 2;
    }
}

大数据量场景优化(可选)

如果数据量极大,内存计算存在性能瓶颈,可针对特定数据库使用原生中位数函数(如SQL Server的PERCENTILE_CONT、PostgreSQL的PERCENTILE_CONT),通过原生SQL查询实现,但会失去跨数据库兼容性。示例(SQL Server):

// 需提前在SalesTrendDTO中添加MedianPricePerSqft和ListAvgPricePerSqft字段
var sqlQuery = @"
SELECT 
    c.Name AS Label,
    c.NameAr AS LabelAr,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY t.AmountSqft) OVER (PARTITION BY c.Name, c.NameAr) AS MedianPricePerSqft,
    STRING_AGG(t.AmountSqft, ',') AS AmountSqftString
FROM Transactions t
JOIN Cities c ON t.CityId = c.Id
WHERE {your_conditions_here}
GROUP BY c.Name, c.NameAr";

var rawResult = await _db.SalesTrendDTO.FromSqlRaw(sqlQuery, /* 传入查询参数 */).ToListAsync();

// 将字符串格式的列表转换为List<decimal>
var finalResult = rawResult.Select(item => 
{
    item.ListAvgPricePerSqft = item.AmountSqftString.Split(',')
                                                   .Select(decimal.Parse)
                                                   .ToList();
    return item;
}).ToList();

内容的提问来源于stack exchange,提问作者Khalil Saqib

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 09:15:09