如何将这个linq缩短为sql查询?

时间:2010-01-05 05:12:01

标签: c# linq linq-to-sql

我正在制作一个日历,为了让自己更容易,我分手了几个星期的约会。

例如1月1日到1月31日跨越6周(我的日历总是42个单元格 - 6乘7)。所以我基本上会在我的数据库中存储6行。

然而,有些事情我需要把所有这些行重新组合成一行。例如,如果我想以Ical格式导出我的日历。

我的数据库中有一个名为bindingClassName的字段,所有这些行都获得了与该组任务相同的unquie id,因此我可以轻松地获得所有周。

// get all of the task rows by binding class name.
            var found = plannerDb.Calendars.Where(u => u.UserId == userId && u.BindingClassName == bindingClassName)
                .GroupBy(u => u.BindingClassName);



            List<Calendar> allAppoingments = new List<Calendar>();

            // go through each of the results and add it to a list of calendars
            foreach (var group in found)
            {

                foreach (var row in group)
                {

                    Calendar appointment = new Calendar();
                    appointment.AppointmentId = row.AppointmentId;
                    appointment.AllDay = row.AllDay;
                    appointment.BindingClassName = row.BindingClassName;
                    appointment.Description = row.Description;
                    appointment.EndDate = row.EndDate;
                    appointment.StartDate = row.StartDate;
                    appointment.Title = row.Title;
                    appointment.Where = row.Where;
                    appointment.UserId = row.UserId;

                    allAppoingments.Add(appointment);
                }
            }
            // order 
           var test = allAppoingments.OrderBy(u => u.StartDate);

           var firstAppointment = test.First();
           var LastAppointment = test.Last();

           Calendar newAppointment = new Calendar();
           newAppointment.UserId = firstAppointment.UserId;
           newAppointment.Description = firstAppointment.Description;
           newAppointment.AllDay = firstAppointment.AllDay;
           newAppointment.StartDate = firstAppointment.StartDate;
           newAppointment.Title = firstAppointment.Title;
           newAppointment.Where = firstAppointment.Where;
           newAppointment.BindingClassName = firstAppointment.BindingClassName;
           newAppointment.EndDate = LastAppointment.EndDate;

            return newAppointment;

基本上,大blob找到具有相同绑定名称的所有约会。然后我浏览每一个并将其变成一个Calendar对象,然后一旦完成,我得到第一个和最后一个记录来获取startDate和endDate。

所以我对linq并不擅长,但我不确定我是否可以在groupBy之后添加一些东西来做我想做的事。

修改

一旦我从用户那里得到所有约会,我正在尝试将所有约会组合在一起。

所以到目前为止我有这个

我试过这样的事情。

    var allApointments = calendar.GetAllAppointments(userId);
    var group = allApointments.GroupBy(u => u.BindingClassName).Select(u => new Calendar()).ToList

我希望它会自动填充每个组,但事实并非如此。所以我不确定是否不再需要groupby。

编辑@ admin

非常感谢您解释排序和分组。你怎么解释它虽然似乎任何人都会工作。

就像你获得第一个和最后一个日期的代码一样,并且做了我想要的。

我认为分组可能有效,因为最后虽然我只想拥有一行具有第一个记录的开始日期和最后一个记录的结束日期,但所有其他信息都是相同的。

所以我不知道是否会更难写这个或者更像是我说你的查询做了我想要的。

但是,该查询仅在一个基础上使用。就像我只在用户点击我的日历上查看该约会时才使用该查询。通过点击约会,我可以获得有关该约会的所有信息,如果该任务跨越多天,我需要查看该信息,并确定约会何时开始以及何时结束。

现在我需要另一个查询,我认为如果我真的可以将它们组合起来会更好,因为我从你的解释中理解它会产生一行。我认为这是因为我想从该用户导出表中的所有记录。

因此,如果我通过绑定名称将它们命令为一个继续块,我仍然需要一些遍历所有记录的循环并获取第一个和开始日期。因此,如果我可以一次性对它进行分组,最终结果只是每组绑定名称的一条记录,它将具有第一个开始日期,并且第一个和最后一个记录的最后结束日期会更好。

2 个答案:

答案 0 :(得分:4)

如果您实际上没有使用该组,为什么要对约会进行分组?看起来你只是单独使用它们。在任何情况下,您已经在BindingClassName子句中的Where的单个值上过滤了行,因此您最终只会得到1(或0)个组。

您可以将foreach一系列Select循环重写为ToList()var allAppointments = plannerDb.Calendars.Where( row => row.UserId == userId && row.BindingClassName == bindingClassName).OrderBy( row => row.StartDate).Select( row => new Calendar() { AppointmentId = row.AppointmentId, AllDay = row.AllDay, BindingClassName = row.BindingClassName, Description = row.Description, EndDate = row.EndDate, StartDate = row.StartDate, Title = row.Title, Where = row.Where, UserId = row.UserId }).ToList(); ,如下所示:

var baseQuery = 
        plannerDb.Calendars.Where(
        row => row.UserId == userId && 
               row.BindingClassName == bindingClassName);

var first = baseQuery.OrderBy(row => row.StartDate).First();
var last = baseQuery.OrderByDescending(row => row.StartDate).Select(
           row => row.EndDate).First();

return new Calendar()
{
    AppointmentId = first.AppointmentId,
    AllDay = first.AllDay,
    BindingClassName = first.BindingClassName,
    Description = first.Description,
    EndDate = last,
    StartDate = first.StartDate,
    Title = first.Title,
    Where = first.Where,
    UserId = first.UserId
});

这将按照您想要的顺序返回完整列表。但是,我很好奇为什么你在看起来时检索整个列表,就像你只对第一个和最后一个约会感兴趣一样。你可以这样做:

var last = baseQuery.OrderByDescending(row => row.EndDate).Select(
           row => row.EndDate).First();

这应该产生与你现在相同的输出。但是,如果这正是你想要的,我会质疑。假设您有两个约会:

  • 任命1从1月5日开始,到1月10日结束
  • 任命2从1月6日开始到1月7日结束

使用这个(和你的)逻辑,你会得到结束日期为1月7日,因为约会2有更大的开始日期,但约会1实际上结束了。我建议将第二个查询更改为:

OrderBy(row => row.BindingName)

这将为您提供最大的结束日期,我认为这是您真正想要的。

修改

我认为你正在制造(非常常见的)将分组与分类混淆的错误。当你说你想“通过绑定名称对约会进行分组”时,听起来你想要一个完整,完整的约会列表,并且你希望这些约会的排列方式与所有具有特定绑定名称的约会形成一个连续的块。如果是这种情况,您希望按绑定名称对列表进行排序,而不是对它们进行分组。分组采用整个列表并为每个分组子句生成一行,并允许您对其余列执行聚合功能。例如,假设我将约束分组在绑定名称上。这意味着我的结果集将包含每个绑定名称一行,然后我可以执行诸如查找最大开始日期或结束日期之类的事情;更正式地说,您可以指定聚合操作,这些操作采用一组数据(即开始日期列表)并返回单个数据(即最大开始日期)。

除非我误解,否则听起来你仍然想要检索所有单独的作业,你只需要通过绑定名称来安排它们。如果是这种情况,只需{{1}}就可以了。此外,您可能希望避免使用“组”这个词,因为人们会认为您的意思是我上面描述的那种分组。

答案 1 :(得分:0)

正如关于linq的一个侧面点,你看过AutoMapper吗?我目前正在使用它来填充来自linq的数据对象,我发现它对于摆脱你刚刚映射到dtos的大部分代码非常有用。它不会使代码的查询部分更短,但会减少:

return new Calendar()
{
    AppointmentId = first.AppointmentId,
    AllDay = first.AllDay,
    BindingClassName = first.BindingClassName,
    Description = first.Description,
    EndDate = last,
    StartDate = first.StartDate,
    Title = first.Title,
    Where = first.Where,
    UserId = first.UserId
});

为:

return Mapper.Map(first,new Calendar{EndDate = last});