简体   繁体   English

Pivot 表从 sql 到 linq

[英]Pivot table from sql to linq

I have following two tables Events and EventType我有以下两个表EventsEventType

I need to do something in order to present my columns EventType names (Direct beneficiaries, Capacity Development, Reach, Online Reach) and Total column which is the sum of all these columns (in particular row):我需要做一些事情来显示我的列 EventType 名称(直接受益人、能力开发、Reach、Online Reach)和 Total 列,它是所有这些列的总和(特别是行):

结果

My sql tables are structered as it is on this image:我的 sql 表是结构化的,就像在这张图片上一样: SQL 表

*Note: If there no eventTypeId in table Events, then, don't show in pivot table that column. *注意:如果Events表中没有eventTypeId,则pivot表中不显示该列。

Those numbers presents TotalAttendants for all events that held up in january, february, etc.这些数字代表 1 月、2 月等举行的所有活动的 TotalAttendants。

If there no cumulative events in March, that month should be empty (March will be here also, but instead of numbers, just empty spaces).如果三月没有累积事件,则该月应该是空的(三月也会在这里,但不是数字,只是空格)。

I tried following linq expression:我尝试遵循 linq 表达式:

var response = _context.Events
                    .Where(x => query.ProjectId.HasValue ? x.ProjectId.Equals(query.ProjectId) : true)
                    .Where(x => x.Date.Year.Equals(query.Year))
                    .Where(x => !x.IsDeleted)
                    .GroupBy(x => new { Year = x.Date.Year, Month = x.Date.Month })
                    .Select(x => new
                    {
                        DirectBeneficiaries = x.Where(m => m.EventTypeId == Domain.Enums.EventType.DirectBeneficiaries)
                            .Select(u => new EventTotal
                            {
                                Month = u.Date.Month.ToString("MMMM"),
                                FemaleAttendants = x.Sum(i => i.FemaleAttendants),
                                MaleAttendants = x.Sum(i => i.MaleAttendants),
                                TotalAttendants = x.Sum(i => i.TotalAttendants)
                            }),
                        CapacityDevelopment = x.Where(m => m.EventTypeId == Domain.Enums.EventType.CapacityDevelopment)
                            .Select(u => new EventTotal
                            {
                                Month = u.Date.Month.ToString("MMMM"),
                                FemaleAttendants = x.Sum(i => i.FemaleAttendants),
                                MaleAttendants = x.Sum(i => i.MaleAttendants),
                                TotalAttendants = x.Sum(i => i.TotalAttendants)
                            }),
                        Reach = x.Where(m => m.EventTypeId == Domain.Enums.EventType.Reach)
                            .Select(u => new EventTotal
                            {
                                Month = u.Date.Month.ToString("MMMM"),
                                FemaleAttendants = x.Sum(i => i.FemaleAttendants),
                                MaleAttendants = x.Sum(i => i.MaleAttendants),
                                TotalAttendants = x.Sum(i => i.TotalAttendants)
                            }),
                        OnlineReach = x.Where(m => m.EventTypeId == Domain.Enums.EventType.OnlineReach)
                            .Select(u => new EventTotal
                            {
                                Month = u.Date.Month.ToString("MMMM"),
                                FemaleAttendants = x.Sum(i => i.FemaleAttendants),
                                MaleAttendants = x.Sum(i => i.MaleAttendants),
                                TotalAttendants = x.Sum(i => i.TotalAttendants)
                            })
                    });

but Im getting anonymous type at the end, and I dont know how to cast to my EventTotal model:但我最后得到匿名类型,我不知道如何转换到我的EventTotal model:

    public class EventTotal
    {
        public string Month { get; set; }
        public int? FemaleAttendants { get; set; }
        public int? MaleAttendants { get; set; }
        public int? TotalAttendants { get; set; }
    }

My questions is: how to convert it to a specific model?我的问题是:如何将其转换为特定的 model?

Update #1:更新#1:

I tried with this code but I have an db exception.我尝试使用此代码,但我有一个 db 异常。

"Column 'Events.TotalAttendants' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.\r\nThe multi-part identifier \"e.Date\" could not be bound.\r\nThe multi-part identifier \"e.Date\" could not be bound.\r\nThe multi-part identifier \"e.Date\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound.\r\nThe multi-part identifier \"e.EventTypeId\" could not be bound."

this is the code这是代码

var eligibleEvents = _context.Events
        .Where(x => query.ProjectId.HasValue ? x.ProjectId.Equals(query.ProjectId) : true)
        .Where(x => x.Date.Year.Equals(query.Year))
        .Where(x => !x.IsDeleted)
        .GroupBy(x => new { Year = x.Date.Year, Month = x.Date.Month, EventType = (int)x.EventTypeId });

var eventTotals = eligibleEvents.GroupBy(x => new { Year = x.Key.Year, Month = x.Key.Month },

    (yearMonthCombination, eventsWithThisYearMonthCombination) => new EventTotal
    {
        Month = yearMonthCombination.Month,
        CapacityDevelopment = eventsWithThisYearMonthCombination.Where(u => u.Key.EventType == (int)Domain.Enums.EventType.CapacityDevelopment).Select(u => u.Sum(s => s.TotalAttendants)).Sum(),
        DirectBeneficiaries = eventsWithThisYearMonthCombination.Where(u => u.Key.EventType == (int)Domain.Enums.EventType.DirectBeneficiaries).Select(u => u.Sum(s => s.TotalAttendants)).Sum(),
        OnlineReach = eventsWithThisYearMonthCombination.Where(u => u.Key.EventType == (int)Domain.Enums.EventType.OnlineReach).Select(u => u.Sum(s => s.TotalAttendants)).Sum(),
        Reach = eventsWithThisYearMonthCombination.Where(u => u.Key.EventType == (int)Domain.Enums.EventType.Reach).Select(u => u.Sum(s => s.TotalAttendants)).Sum(),
        Total = eventsWithThisYearMonthCombination.Select(u => u.Sum(s => s.TotalAttendants)).Sum(),
        TotalAttendants = eventsWithThisYearMonthCombination.Select(u => u.Sum(s => s.TotalAttendants)).Sum()                        
    }).ToList();

return eventTotals;

So from your sequence of Events , you want to extract a sequence of EventTotals : one EventTotal per group of Events that are from the same [Year, Month].因此,从您的Events序列中,您想要提取一系列EventTotals :每组来自相同 [Year, Month] 的 EventTotal。

  • Well, not from all Events, only those that are not Deleted好吧,不是所有事件,只有那些没有被删除的事件
  • And from those that were not deleted, you only want the ones that match the ProjectId and the Year而那些没有被删除的,你只想要那些与 ProjectId 和 Year 匹配的

First of all: we can optimize the start of your query: put everything in one Where :首先:我们可以优化查询的开始:将所有内容放在一个Where

var eligibleEvents = dbContext.Events
    .Where(event => !event.IsDeleted
        && event.Date.Year == query.Year)
        && (!query.ProjectId.HasValue || query.ProjectId == event.ProjectId)

In words: from all Events, keep only those events that are not deleted yet, and that have a Date.Year that equals query.Year .换句话说:从所有事件中,只保留那些尚未删除且Date.Year等于query.Year的事件。 Depending on whether query.ProjectId is null or not, you keep all these events, or you keep only those events with a value for ProjectId that equals query.ProjectId根据query.ProjectId是否为 null,您保留所有这些事件,或者仅保留ProjectId的值等于query.ProjectId的那些事件

Consider to simplify the OR as follows:考虑将 OR 简化如下:

(query.ProjectId == null) || (query.ProjectId == event.ProjectId)

Now from all remaining Events , we make groups of Events.现在从所有剩余的Events中,我们制作事件组。 All Events that have the same [Year, Month] combination will be in one group.具有相同 [年、月] 组合的所有事件将在一个组中。 From every Group we make exactly one EventTotal .从每个组中,我们只制作一个EventTotal

If you use GroupBy, and you want to create one object from every Group, like we do, consider to use the overload of GroupBy that has a parameter resultSelector如果你使用 GroupBy,并且你想从每个 Group 中创建一个 object,就像我们一样,考虑使用GroupBy 的重载,它有一个参数 resultSelector

Continuing the LINQ:继续 LINQ:

var eventTotals = eligibleEvents.GroupBy(event => new
{
    Year = event.Date.Year,
    Month = event.Date.Month,
},

// parameter resultSelector: from every [Year, Month] combination,
// and all eligible events that have these values of Year / Month,
// make one new EventTotal:
(yearMonthCombination, eventsWithThisYearMonthCombination) => new EventTotal
{
    Month = yearMonthCombination.Month,

    // Count the zero or more FemaleAttendants:
    FemaleAttendants = eventsWithThisYearMonthCombination
        .Select(event => event.FemaleAttendants)
        .Sum(),

    // Count the zero or more MaleAttendants:
    MaleAttendants = eventsWithThisYearMonthCombination
        .Select(event => event.MaleAttendants)
        .Sum(),

    TotalAttendants = eventsWithThisYearMonthCombination
        .Select(event => event.TotalAttendants)
        .Sum(),
}

If TotalAttendants is always the sum of the FemaleAttendants and MaleAttendants, why is it a separate column?如果TotalAttendants始终是 FemaleAttendants 和 MaleAttendants 的总和,为什么它是一个单独的列? Consider to change the property:考虑更改属性:

public int TotalAttendants => this.FemaleAttendants + this.MaleAttendants;

By the way, why are your attendants nullable?顺便问一下,为什么你的随从可以为空? is there a difference between zero attendants and null attendants?零服务员和null服务员有区别吗? If not, consider to remove the nullable, you only make life more difficult for you.如果没有,请考虑删除可为空的,只会让您的生活更加困难。

If you really want if nullable, when should zero attendants be converted to null?如果你真的想要可以为空,那么零服务员什么时候应该转换为 null? Alas you forgot to define this.唉,你忘了定义这个。

This won't fill your table这不会填满你的桌子

You specifically asked for EventTotals .您特别要求EventTotals I don't see how this would help you to fill the table.我不明白这将如何帮助您填写表格。

If you want a row with [Month, DirectBenificaries, CapacityDevelopment, ...], I would go for a different resultSelector:如果你想要一个带有 [Month, DirectBenificaries, CapacityDevelopment, ...] 的行,我会使用 go 换一个不同的 resultSelector:

(yearMonthCombination, eventsWithThisYearMonthCombination) => new
{
    Month = yearMonthCombination.Month,

    DirectBenificiaries = eventsWithThisYearMonthCombination
        .Where(event => event.EventTypeId == dbContext.EventTypes
                        .Where(eventType => eventType.Name == "DirectBenificiaries")
        .Select(event => ...)
        .Sum(),

Alas, you didn't tell us what property you use to make up the DirectBenificiaries of Jan 2020. Is it the total number of events with type DirectBenificiaries?唉,您没有告诉我们您使用什么属性来构成 2020 年 1 月的 DirectBenificiaries。它是 DirectBenificiaries 类型的事件总数吗? Or is if the total number of Attendants in January 2020 with DirectBenificiaries?或者是 2020 年 1 月 DirectBenificiaries 的服务员总数?

The other columns are similar.其他列类似。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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