SQL 通过/左连接按组获取最大 n 的所有行

标签 sql entity-framework linq

目标:获取所有产品、最新季度的总销售额、上一季度的总销售额以及所有季度的平均销售额

QuarterlyReport {
    Id
    EndDate
    ProductId
}

SalesRecord {
    QuarterlyReportId
    SalesAmount
}

Product {
    Id
}

到目前为止我们所拥有的:

获取所有 QuarterlyReports 以及所有关联记录的 SalesAmount 的总和,使用它来查询我们的其余数据

With QRU as (
    SELECT QuarterlyReport.ProductId as productId, QuarterlyReport.EndDate as 
    reportEndDate, SUM(SalesRecord.SalesAmount) as QuarterlyTotalSales
    FROM table_QuarterlyReports as QuarterlyReport LEFT JOIN table_SalesRecords 
    as SalesRecord on QuarterlyReport.Id = SalesRecord.QuarterlyReportId
    Group BY QuarterlyReport.ProductId, QuarterlyReport.EndDate
)

Select DISTINCT product.*, 
(select avg(QuarterlyTotalSales) from QRU where QRU.productId = product.Id) as averageSales,
(select TOP 1 QuarterlyTotalSales from QRU where QRU.productId = product.Id 
ORDER BY reportEndDate DESC) as ThisQuarterSales,
(select QuarterlyTotalSales from QRU where QRU.productId = product.Id ORDER 
BY reportEndDate DESC OFFSET 1 rows fetch next 1 rows only) as LastQuarterSales
From table_Products as product

这是模拟表:

GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[table_Products](
    [Location] [int] NOT NULL,
    [Id] [int] IDENTITY(1,1) NOT NULL)
GO

CREATE TABLE [dbo].[table_QuarterlyReports](
    [EndDate] [datetime] NOT NULL,
    [ProductId] [int] NOT NULL,
    [Id] [int] IDENTITY(1,1) NOT NULL)
GO

CREATE TABLE [dbo].[table_SalesRecords](
    [QuarterlyReportId] [int] NOT NULL,
    [SalesAmount] [bigint] NOT NULL,
    [Id] [int] IDENTITY(1,1) NOT NULL)
GO

INSERT INTO [dbo].[table_Products] VALUES (1);
INSERT INTO [dbo].[table_Products] VALUES (2);
INSERT INTO [dbo].[table_Products] VALUES (3);

INSERT INTO [dbo].[table_QuarterlyReports] VALUES ('20170502 12:00:00 AM', 1)
INSERT INTO [dbo].[table_QuarterlyReports] VALUES ('20170502 12:00:00 AM', 2)
INSERT INTO [dbo].[table_QuarterlyReports] VALUES ('20170502 12:00:00 AM', 3)
INSERT INTO [dbo].[table_QuarterlyReports] VALUES ('20170402 12:00:00 AM', 1)
INSERT INTO [dbo].[table_QuarterlyReports] VALUES ('20170402 12:00:00 AM', 2)
INSERT INTO [dbo].[table_QuarterlyReports] VALUES ('20170402 12:00:00 AM', 3)

INSERT INTO [dbo].[table_SalesRecords] VALUES (1, 1000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (1, 2000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (2, 7000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (2, 1000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (3, 2000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (3, 3000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (4, 5000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (4, 4000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (5, 4000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (5, 2000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (6, 4000);
INSERT INTO [dbo].[table_SalesRecords] VALUES (6, 3000);

这是可行的,尽管我们希望它全部包含在一个查询中。此外,我们正在尝试将此查询转换为 Entity Framework LINQ to Entities。

最佳答案

可以通过联接来完成,但在 EF 中,您最好定义和使用导航属性。

以下是具有集合导航属性的示例模型:

[Table("table_Products")]
public class Product
{
    public int Id { get; set; }
    public int Location { get; set; }
    // Navigation properties
    [ForeignKey("ProductId")]
    public ICollection<QuarterlyReport> QuarterlyReports { get; set; }
}

[Table("table_QuarterlyReports")]
public class QuarterlyReport
{
    public int Id { get; set; }
    public DateTime EndDate { get; set; }
    public int ProductId { get; set; }
    // Navigation properties
    [ForeignKey("QuarterlyReportId")]
    public ICollection<SalesRecord> SalesRecords { get; set; }
}

[Table("table_SalesRecords")]
public class SalesRecord
{
    public int Id { get; set; }
    public int QuarterlyReportId { get; set; }
    public long SalesAmount { get; set; }
}

那么等效的 LINQ to Entities 查询可能如下所示:

var query =
    from p in db.Products
    let sales =
        from qr in p.QuarterlyReports // inner join
        from sr in qr.SalesRecords.DefaultIfEmpty() // left join
        group sr by qr.EndDate into srg
        select new
        {
            Date = srg.Key,
            Amount = (from sr in srg select (long?)sr.SalesAmount).Sum()
        }
    select new
    {
        p.Id,
        p.Location,
        AverageSales = (from s in sales select s.Amount)
            .Average(),
        ThisQuarterSales = (from s in sales orderby s.Date descending select s.Amount)
            .FirstOrDefault(),
        LastQuarterSales = (from s in sales orderby s.Date descending select s.Amount)
            .Skip(1).FirstOrDefault(),
    };

关于SQL 通过/左连接按组获取最大 n 的所有行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43787290/

相关文章:

c# - LINQ to Entities 无法识别该方法

c# - 在 c# linq 中转换为值类型 'System.Single' 失败

c# - 如何在 C# 中的 lambda 表达式中查找数组中元素的索引

java - 通过java更改mysql密码

sql - Oracle - 获取字符第 n 次出现之前的所有字符

sql - Postgresql - 从对象数组中提取字段到文本数组

c# - ID 不同的 Linq 计数

mysql - 选择与三个表不同的查询

c# - Linq输出相乘结果

entity-framework - 与ServiceStack Orm lite相比,Entity Framework 5的性能