Skip to content

Improve GroupBy translation for JSON-owned properties to avoid N scalar subqueries #37778

@mohsen-mars

Description

@mohsen-mars

What problem are you trying to solve?

Problem

When grouping and aggregating owned JSON properties, EF Core translates each aggregate into a correlated scalar subquery instead of a single GROUP BY aggregation.

This results in multiple scans over the same dataset and significantly degrades performance for large datasets.


Example Model

public class InvoiceDb
{
    public int Id { get; set; }
    public SummaryDb Summary { get; set; }
}

[NotMapped]
public class SummaryDb
{
    public long TotalAmount { get; set; }
    public long TotalDiscount { get; set; }
}

Mapping (JSON column)

builder.OwnsOne(p => p.Summary, b =>
{
    b.ToJson();
    b.Property(x => x.TotalAmount);
    b.Property(x => x.TotalDiscount);
});

LINQ Query

var result = await dbContext.Invoices
    .GroupBy(x => x.CreatedAt.Date)
    .Select(g => new
    {
        Date = g.Key,
        TotalAmount = g.Sum(x => x.Summary.TotalAmount),
        TotalDiscount = g.Sum(x => x.Summary.TotalDiscount),
    })
    .ToListAsync();

Generated SQL (simplified)

SELECT [s].[Key],
    (SELECT SUM(JSON_VALUE(...TotalAmount...)) ...) AS TotalAmount,
    (SELECT SUM(JSON_VALUE(...TotalDiscount...)) ...) AS TotalDiscount
FROM (...)
GROUP BY [s].[Key];

Each aggregate is translated into a separate correlated scalar subquery, causing multiple scans of the same data.

Impact

This affects reporting and analytical workloads and makes aggregation over JSON-owned properties impractical for large datasets.
It also limits adoption of JSON-owned types in real-world reporting scenarios where server-side aggregation is required.

Describe the solution you'd like

Suggested Behavior

Translate to a single GROUP BY with all JSON_VALUE projections in one pass, e.g.:

SELECT CAST(i.InvoiceDate AS date) AS [Date],
       SUM(CAST(JSON_VALUE(i.Summary, '$.TotalAmount') AS bigint)) AS TotalAmount,
       SUM(CAST(JSON_VALUE(i.Summary, '$.TotalDiscount') AS bigint)) AS TotalDiscount
FROM Invoices i
GROUP BY CAST(i.InvoiceDate AS date);

Environment

  • EF Core version: 10.0.0
  • Database provider: Microsoft.EntityFrameworkCore.SqlServer
  • .NET SDK: .NET 10.0

Metadata

Metadata

Assignees

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions