将SQL分组查询转换为LINQ查询

时间:2016-11-28 12:24:21

标签: c# sql-server linq group-by

我在尝试将SQL查询转换为LINQ时遇到了麻烦。 假设,我们有以下数据结构:

class Movie
{
    public Guid ID { get; set; }

    // navigation properties
    public virtual ICollection<Commercial> Commercials { get; set; }
    public virtual ICollection<Spectator> Spectators { get; set; }
}

class Commercial
{
    public Guid ID  { get; set; }
    public Guid MovieID { get; set; }
    public string ProductType { get; set; }

    // navigation property
    public virtual Movie Movie { get; set; }
}

class Spectator
{
    public Guid ID  { get; set; }
    public Guid MovieID { get; set; }
    public int Age { get; set; }

    // navigation property
    public virtual Movie Movie { get; set; }
}

现在让我们说,我想知道,有多少观众看过某个产品类别的广告。在SQL中,它看起来像这样:

select Commercial.ProductType, count(distinct Spectator.ID)
from Spectator
join Movie on Spectator.MovieID = Movie.ID
join Commercial on Commercial.MovieID = Movie.ID
where Spectator.Age > 60 # optional filter
group by Commercial.ProductType;

首先,我尝试使用GroupBy()功能,但由于多对多关系,我没有找到按商业产品类型对观众进行分组的方法。

然后我尝试了类似的东西:

var query = db.Commercials.Where(x => x.Age > 60).GroupJoin(
    db.Spectators,
    c => c.MovieID,
    s => s.MovieID,
    (c, g) => new { ProductType = c.ProductType, Count = g.Distinct().Count() });

这看起来很有希望,但没有回复预期的结果。

3 个答案:

答案 0 :(得分:1)

您拥有所有这些不错的导航属性,因此您无需加入LINQ。导航属性可以被视为硬编码连接,可以防止重复,冗长和容易出错的代码(例如,使用错误的连接属性)。

了解这一点,您可以考虑使用查询来获取数据。它并不像看起来那么微不足道(而且正如我最初想的那样)。

在一部电影中可能会有n个广告,所以如果您只计算每部电影和广告的观众数量,结果太高(观众人数的n倍)。你必须计算独特的观众。这些计数应按ProductType分组。这就把我们带到了这个问题:

var query = from c in db.Commercials
            group c by c.ProductType into cgroup
            select new
            {
                ProductType = cgroup.Key,
                NumberOfSpectators = cgroup.SelectMany(c => c.Movie.Spectators
                     .Where(s => s.Age > 60)
                     .Select(s => s.Id)).Distinct()).Count()
            };

答案 1 :(得分:0)

你的数据库之间有关联吗? 如果是,您的实体框架生成的模型必须如下:

 public class Receiver
    {
        public int LetterId { set; get; }
        public Letter Letter { set; get; }
        public Country Country { set; get; }
    }
    public class Letter
    {
        public int Id { set; get; }
        public int SenderId { set; get; }
        public Sender Sender { set; get; }
        public IEnumerable<Receiver> Receivers { set; get; }

    }
    public class Sender
    {
        public int Id { set; get; }
        public Country Country { set; get; }
        public IEnumerable<Letter> Letters { set; get; }
    }
    public class Country
    {
        public int Id { set; get; }
    }

然后你的上下文有3种类型:

IEnumerable<Receiver> receivers = new List<Receiver>();
IEnumerable<Letter> leters = new List<Letter>();
IEnumerable<Sender> senders = new List<Sender>();

所以你的回答是这样的:

var results = from receiver in receivers
        from letter in leters
        from sender in senders
        where receiver.LetterId == letter.Id &&
              sender.Id == letter.SenderId
        select
            new Result
            {
                Country = sender.Country,
                CountOfCountry = sender.Letters.Select(x => x.Receivers).Distinct().Count()
            };

ResultClass是:

public class Result
    {
        public Country Country { set; get; }
        public int CountOfCountry{ set; get; }
    }

如果你把你的班级图表,我可以帮助你更好!

答案 2 :(得分:0)

我做了一些小样本。

public class Letter
{
    public int Id { get; set; }
    public int SenderId { get; set; }
}
public class Sender
{
    public int Id { get; set; }
    public string Country { get; set; }
}
public class Receiver
{
    public int Id { get; set; }
    public int LetterId { get; set; }
    public string Country { get; set; }
}
class StackOverflow_SQLtoLinq
{
    static void Main(string[] args)
    {
        List<Letter> lstLetters = new List<Letter>() { 
        new Letter(){Id=1,SenderId=1},
        new Letter(){Id=2,SenderId=2},
        new Letter(){Id=3,SenderId=3}
        };

        List<Sender> lstSenders = new List<Sender>() {
        new Sender(){Id=1,Country="IND"}, 
        new Sender(){Id=2,Country="US"},  
        new Sender(){Id=3,Country="NZ"}
        };

        List<Receiver> lstReceivers = new List<Receiver>() { 
        new Receiver(){Id=1,LetterId=1,Country="IND"},
        new Receiver(){Id=2,LetterId=11,Country="US"},
        new Receiver(){Id=3,LetterId=1,Country="NZ"},
        };

        var data = (from receiver in lstReceivers
                   join letter in lstLetters on receiver.LetterId equals letter.Id 
                   join sender in lstSenders on letter.SenderId equals sender.Id
                    group sender by new { id = sender.Id, country = sender.Country } into finalData
                   select new
                   {
                       country = finalData.Key.country,
                       Count = finalData.Distinct().Count()
                   }).ToList();

    }
}

最后在数据变量中有数据。