【问题标题】:Convert simple Left Outer Join and group by SQL statement into Linq将简单的 Left Outer Join 和 group by SQL 语句转换为 Linq
【发布时间】:2015-10-09 21:06:22
【问题描述】:

2 个表: 用户和警报

表:用户 用户 ID(整数), 全名(varchar)

表格:警报 分配给(int), 已解决(布尔)

查询:

SELECT u.Fullname, COUNT(resolved) as Assigned, SUM(CONVERT(int,Resolved)) as Resolved, COUNT(resolved) -  SUM(CONVERT(int,Resolved)) as Unresolved
FROM Alarm i LEFT OUTER JOIN Users u on i.AssignedTo = u.UserID
GROUP BY u.Fullname

结果:

Fullname  Assigned  Resolved  Unresolved
User1     204       4         200
User2     39        9         30
User3     235       200       35
User4     1         0         1
User5     469       69        400

对于我的生活,我无法弄清楚如何将它变成一个 Linq 查询。我在使用分组功能时遇到问题。 我看过无数的例子,没有一个例子有我的左外连接与分组的组合,或者它们太复杂了,我不知道如何让它与我的一起工作。这里的任何帮助将不胜感激!!!

更新: 我可能不清楚我在寻找什么。我正在寻找按用户 ID 的 AssignedTo 列分组的警报...除了,我想用位于 users 表中的 FullName 替换该用户 ID。有人发布并删除了一些接近的东西,除了它给了我用户表中的所有用户,这不是我想要的......

更新 2:在下面查看我的答案

【问题讨论】:

  • 报警表有主键吗?
  • 你知道你的 WHERE 子句把你的 LEFT JOIN 变成了一个 INNER JOIN
  • 是的,它是“id”,但在 sql 查询中不需要,因为我们正在加入警报表中的 AssignedTo 字段和 User 表中的 UserId 字段。
  • 这是另一个来自 MSDN 的 example。您是否得到错误的结果或错误消息?
  • 哇!甚至没有意识到于尔根!但是,这确实给了我需要的结果。那么如何在 linq 查询中执行此操作? :)

标签: c# sql-server linq join linq-to-entities


【解决方案1】:

假设你有以下模型:

这是警报的模型:

public class Alarm
{
    public int id { get; set; }

    public int AssignedTo { get; set; }

    [ForeignKey("AssignedTo")] 
    public virtual User User { get; set; }

    public bool Resolved { get; set; }
}

这是用户的模型:

public class User
{
    public int UserID { get; set; }

    public string FullName { get; set; }

    public virtual ICollection<Alarm> Alarms { get; set; }

    public User()
    {
        Alarms = new HashSet<Alarm>();
    }
}

这是为每个用户保存警报统计信息的模型:

public class UserStatistics
{
    public string FullName { get; set; }
    public int Assigned { get; set; }    
    public int Resolved { get; set; }    
    public int Unresolved { get; set; }    
}

然后您可以执行以下操作:

var query = context.Users.Select(
    user =>
        new UserStatistics
        {
            FullName = user.FullName,
            Assigned = user.Alarms.Count,
            Resolved = user.Alarms.Count(alarm => alarm.Resolved),
            Unresolved = user.Alarms.Count(alarm => !alarm.Resolved)
        });


var result = query.ToList();

对了,你也可以修改查询,去掉Unresolved = user.Alarms.Count(alarm =&gt; !alarm.Resolved),然后把Unresolved属性变成这样的计算属性:

public class UserStatistics
{
    public string FullName { get; set; }
    public int Assigned { get; set; }    
    public int Resolved { get; set; }    
    public int Unresolved
    {
        get { return Assigned - Resolved; }
    }
}

这将使生成的 SQL 查询更简单。

【讨论】:

  • 虽然这非常令人印象深刻,但这里有很多我不明白的地方:) 我的课程绝对不是那样设置的。也许他们应该是,但我需要了解一些事情,比如 Virtual Icollections 以及从哪里调用 User() 函数。你的努力得了A!!! :)
  • 您使用的数据访问技术是什么? (例如实体框架、LINQ to SQL...)。你的模型是什么样的?你是用代码写的吗?还是您从数据库中自动生成的?
  • Massd - 我正在使用实体框架。出于所有意图和目的,我的模型如上所述,但每个表中有更多属性。我已经弄清楚并以 lambda 格式发布了我的答案。我将如何用类似 linq sql 的语法编写它?谢谢!
【解决方案2】:

我终于明白了。

这个:

var results = alarms.GroupBy(x => x.AssignedTo)
.Join(users, alm => alm.Key , usr => usr.UserID, (alm, usr) => new {
    Fullname = usr.FullName,AssignedNum = alm.Count(),
    Resolved = alm.Where(t=>t.resolved == true).Select(y => y.resolved).Count(), 
    Unresolved = alm.Where(t=>t.resolved == false).Select(y => y.resolved).Count() });

转载此:

SELECT u.Fullname, COUNT(resolved) as Assigned, SUM(CONVERT(int,Resolved)) as Resolved, 
       COUNT(resolved) -  SUM(CONVERT(int,Resolved)) as Unresolved
FROM Alarm i LEFT OUTER JOIN Users u on i.AssignedTo = u.UserID
GROUP BY u.Fullname

结果按 AssignedTo (int) 分组,但未选择 AssignedTo。而是从加入的用户表中选择 FullName。

非常感谢所有试图提供帮助的人!我从你的回答中学到了很多。

对于加分,我将如何用类似 SQL 的语法编写我的 lamdbda 答案?

【讨论】:

    【解决方案3】:

    试试这个:

    from u in context.User
    join a in context.Alarm on u.UserID equals a.AssignedTo into g1
    from g2 in g1.DefaultIfEmpty()
    group g2 by u.Fullname into grouped
    select new { Fullname = grouped.Key, Assigned = grouped.Count(t=>t.Resolved != null), Resolved = grouped.Sum
                                        (t => int.Parse(t.Resolved)), Unresolved = (grouped.Count(t=>t.Resolved != null) - grouped.Sum
                                        (t => int.Parse(t.Resolved)))}
    

    【讨论】:

    • 这与我尝试做的类似,但我得到的错误与我在 'group g2 by u.Fullname into grouped' 上得到的错误相同线。 “无法将 lambda 表达式转换为类型 'System.Collections.Generic.IEqualityComparer' 因为它不是委托类型” 并且这个 “'MyProject.Models .User' 不包含“Fullname”的定义,并且找不到接受“MyProject.Models.User”类型的第一个参数的扩展方法“Fullname”(您是否缺少 using 指令或程序集引用?)“
    • 从查询中提取任何 C# 调用(例如 Int32.Parse)并将它们作为常量值传递。如果不能 - 将 LINQ 查询转换为 SQL 是不可能的。所以你需要重新考虑它(或者在你真正知道自己在做什么之前,在记忆中这样做是一个很大的禁忌)。
    【解决方案4】:

    我猜在 Linq 中这个查询不一定要使用“Grouping”,因为“LEFT JOIN”+“GROUP BY”的组合将它们变成了“INNER JOIN”。

        var results =
            from u in users
            join a in alarms on u.UserID equals a.AssignedTo into ua
            select new
            {
                Fullname = u.FullName,
                Assigned = ua.Count(),
                Resolved = ua.Count(a => a.Resolved),
                Unresolved = ua.Count(a => !a.Resolved)
            };
    
            foreach (var r in results)
            {
                Console.WriteLine(r.Fullname + ", " + r.Assigned + ", " + r.Resolved + ", " + r.Unresolved);
            }
    

    【讨论】:

    • 在你真正需要之前不要写显式连接(通常你不需要)。让EF做它的工作:)
    • @abatishchev 请解释一下。
    • @Wizadre:我会有一个导航属性 1:many ICollection 用于用户和用户用于警报。
    猜你喜欢
    • 1970-01-01
    • 2010-12-13
    • 1970-01-01
    • 2021-05-03
    • 1970-01-01
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多