【问题标题】:Left join with grouping and ordering左加入分组和排序
【发布时间】:2012-08-02 07:41:28
【问题描述】:

音乐家写歌。歌曲在空中播放。

我有数据库表 Musicians、Songs 和 AirTimes。 AirTimes 表条目包含有关在哪个日期播放了哪首歌以及播放了多少分钟的信息。

我有与表格相对应的 Musician、Song、AirTime 课程。这些类具有指向另一个实体的导航属性。下面的箭头代表导航。

音乐家 歌曲 通话时间

从数据库中,我必须检索他/她的歌曲获得 AirTime 的所有音乐家和日期。另外,我想显示特定日期播放的歌曲数以及该日期播放的分钟数。

在 Microsoft SQL 中,我会这样做:

select 
    dbo.Musicians.LastName
  , dbo.AirTimes.PlayDate
  , count(dbo.AirTimes.PlayDate) as 'No. of entries'
  , sum(dbo.AirTimes.Duration) as 'No. of minutes'
from dbo.Musicians
  left outer join dbo.Songs
  on dbo.Musicinas.MusicianId = dbo.Songs.MusicianId
    left outer join dbo.AirTimes
    on dbo.Songs.SongId = dbo.AirTimes.SongId
    and '2014-07-01T00:00:00' <= dbo.AirTimes.PlayDate 
    and dbo.AirTimes.PlayDate <= '2014-07-31T00:00:00'
group by 
    dbo.Musicians.LastName
  , dbo.AirTimes.PlayDate
order by
    dbo.Musicians.LastName
  , dbo.AirTimes.PlayDate

任何人都可以将其“翻译”成 linq-to-entitese 吗?

2012 年 8 月 9 日更新
我无法确认 grudolf 的计划是否符合我的要求。我用不同的技术完成了一些事情。尽管如此,我接受他/她的回答。

【问题讨论】:

  • 你试过什么?您卡在哪里或有任何错误消息?很多人会提供帮助,但 SO 不是 LINQ 翻译服务。

标签: linq entity-framework linq-to-entities entity-framework-4.1


【解决方案1】:

由于您在两个方向都有导航属性,因此您可以从 AirTimes 开始:

var grpTime = (
    from a in AirTimes
    where a.Date >= firstDate && a.Date < lastDate
    group a by new {a.Song.Musician.LastName, a.Song.Title, a.Date} into grp
    select new {
        grp.Key.LastName,
        grp.Key.Title,
        grp.Key.Date,
        Plays = grp.Count(),
        Seconds = grp.Sum(x => x.Duration)
    }
);

或来自音乐家:

var grpMus = (
    from m in Musicians
    from s in m.Songs
    from p in s.Plays
    where p.Date >= firstDate && p.Date < lastDate
    group p by new {m.LastName, s.Title, p.Date} into grp
    select new {
        grp.Key.LastName,
        grp.Key.Title,
        grp.Key.Date,
        Plays = grp.Count(),
        Seconds= grp.Sum(x => x.Duration)
    }
);

编辑: 要显示所有音乐家,包括那些没有通话时间的音乐家,您可以使用另一个级别的分组 - 在第一步中,您计算​​每首歌曲+天的总数,然后将它们与歌曲的作者分组。它可能可以直接与数据库一起使用,但我没有设法找到一种有效的方法来做到这一点。然而。 ;) 使用代码,原始的 AirTimes 结果被更改为返回 Musician 而不是他的姓氏,然后加入到所有音乐家的列表中:

//Airtimes for musicians
var grpAir = (
    from a in AirTimes
    where a.Date >= firstDate && a.Date < lastDate
    group a by new {a.Song.Musician, a.Date} into grp
    select new {
        //Musician instead of his LastName for joining. Id would work too
        grp.Key.Musician,
        //grp.Key.Musician.LastName,
        Date=grp.Key.Date,
        Plays = grp.Count(),
        Secs = grp.Sum(x => x.Duration)
    }
);

var res = (
    from m in Musicians
    join g in grpAir on m equals g.Musician into g2
    from g in g2.DefaultIfEmpty()
    orderby m.LastName
    select new {
        m.LastName,
        Date = (g==null ? null : g.Date),
        Plays = (g==null ? 0 : g.Plays),
        Secs = (g==null ? 0 : g.Secs)
    }
);

您可以在 https://gist.github.com/3236238 找到更完整的 LINQPad 示例

【讨论】:

  • grudolf -- 您的第一个方案与我的非常相似。问题是它没有在 Musicians 表上进行左连接,即没有检索到在指定日期范围内没有获得通话时间的音乐家。我需要所有的音乐家。您的第二个方案的问题是 linq 检索每个音乐家的所有歌曲,即即使是那些音乐家不是作者的歌曲。感谢您的努力。
  • 你说得对,如果音乐家没有任何播出时间,他就不会出现在列表中。我添加了一个连接来包含它们并修改了输出。不过,对空日期进行排序并没有太多运气。第二种方案似乎工作正常,至少对于包含的样本数据。也许 Linq to Entities 以不同的方式转换层次结构。
  • grudolf -- 我说你的第二个方案“为每个音乐家检索所有歌曲”是错误的。但是,您的第二个方案不会检索所有音乐家,即它不会在 Musicians 表上执行左连接。
猜你喜欢
  • 2012-03-01
  • 2017-05-30
  • 1970-01-01
  • 1970-01-01
  • 2017-03-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 2018-08-13
相关资源
最近更新 更多