【问题标题】:LINQ many to many Left Join GroupingLINQ多对多左连接分组
【发布时间】:2010-11-21 09:31:48
【问题描述】:

我有一个多对多表关系:

CUS_Phone:拥有自己的唯一 id、来自其父表的 cus id 以及名称标题日期等... CUS_Phone_JCT:拥有自己的唯一id,来自CUS_Phone的id和来自CUS_Phone的id CUS_Phone:拥有自己的唯一 id 和电话号码

这里我有一个连接查询来检索所有客户姓名和电话号码:

var q = from c in CUS_Contact
    join cp in CUS_Phone_JCT on c.Id equals cp.Contact_Id into cp2 
    from cp3 in cp2.DefaultIfEmpty()
    join p in CUS_Phone on cp3.Phone_Id equals p.Id into p2 
    from p3 in p2.DefaultIfEmpty()
    where c.Cus_Id == 9120
    select new
            {
                c.Id,
                c.Cus_Id, 
                c.Namefirst, 
                c.Namemiddle, 
                c.Namelast, 
                cp3.Phone.Phone, 
                c.Title, 
                c.Dept, 
                c.Des, c.Datecreate, 
                c.Dateupdate, 
                c.Usr_idcreate, 
                c.Usr_idupdate
            };

foreach(var v in q){
Console.WriteLine(v.Id + "-" + v.Namefirst + "-" + v.Phone);
}

如何制定查询以对每个客户的数字进行分组?我想看到不同的客户,每个客户都有一个数字列表(IEnumerable List)。 LINQPad spanishOrders 查询中有一个类似的示例,但它们对每个订单的订单详细信息进行分组。我不确定如何使用我的架构来做到这一点。谢谢!


编辑:这是从第一个答案输出的 sql...

-- 区域参数
声明 @p0 内部设置 @p0 = 4
-- 结束区域
选择 (
    选择 [t8].[id]
    从 (
        选择顶部 (1) [t6].[id]
        从 [CUS_Contact] 作为 [t6]
        左外连接 [CUS_Phone_JCT] AS [t7] ON [t6].[id] = [t7].[Contact_Id]
        哪里 [t2].[id] = [t6].[id]
        ) 作为 [t8]
    ) 作为 [Id], (
    选择 [t11].[Cus_Id]
    从 (
        选择顶部 (1) [t9].[Cus_Id]
        从 [CUS_Contact] 作为 [t9]
        左外连接 [CUS_Phone_JCT] AS [t10] ON [t9].[id] = [t10].[Contact_Id]
        哪里 [t2].[id] = [t9].[id]
        ) 作为 [t11]
    ) AS [Cus_Id], (
    选择 [t14].[namefirst]
    从 (
        选择顶部 (1) [t12].[namefirst]
        从 [CUS_Contact] 作为 [t12]
        左外连接 [CUS_Phone_JCT] AS [t13] ON [t12].[id] = [t13].[Contact_Id]
        哪里 [t2].[id] = [t12].[id]
        ) 作为 [t14]
    ) AS [Namefirst], (
    选择 [t17].[namemiddle]
    从 (
        选择顶部 (1) [t15].[namemiddle]
        从 [CUS_Contact] 作为 [t15]
        左外连接 [CUS_Phone_JCT] AS [t16] ON [t15].[id] = [t16].[Contact_Id]
        哪里 [t2].[id] = [t15].[id]
        ) 作为 [t17]
    ) AS [中间名], (
    选择 [t20].[namelast]
    从 (
        选择顶部 (1) [t18].[namelast]
        从 [CUS_Contact] 作为 [t18]
        左外连接 [CUS_Phone_JCT] AS [t19] ON [t18].[id] = [t19].[Contact_Id]
        哪里 [t2].[id] = [t18].[id]
        ) 作为 [t20]
    ) AS [姓氏], (
    选择 [t23].[标题]
    从 (
        选择顶部 (1) [t21].[标题]
        从 [CUS_Contact] 作为 [t21]
        左外连接 [CUS_Phone_JCT] AS [t22] ON [t21].[id] = [t22].[Contact_Id]
        哪里 [t2].[id] = [t21].[id]
        ) 作为 [t23]
    ) 如题], (
    选择 [t26].[部门]
    从 (
        选择顶部 (1) [t24].[部门]
        从 [CUS_Contact] 作为 [t24]
        左外连接 [CUS_Phone_JCT] AS [t25] ON [t24].[id] = [t25].[Contact_Id]
        WHERE [t2].[id] = [t24].[id]
        ) 作为 [t26]
    ) AS [部门], (
    选择 [t29].[des]
    从 (
        选择顶部 (1) [t27].[des]
        从 [CUS_Contact] 作为 [t27]
        左外连接 [CUS_Phone_JCT] AS [t28] ON [t27].[id] = [t28].[Contact_Id]
        哪里 [t2].[id] = [t27].[id]
        ) 作为 [t29]
    ) AS [Des], (
    选择 [t32].[日期创建]
    从 (
        选择顶部 (1) [t30].[datecreate]
        从 [CUS_Contact] 作为 [t30]
        左外连接 [CUS_Phone_JCT] AS [t31] ON [t30].[id] = [t31].[Contact_Id]
        哪里 [t2].[id] = [t30].[id]
        ) 作为 [t32]
    ) AS [日期创建], (
    选择 [t35].[日期更新]
    从 (
        选择顶部 (1) [t33].[dateupdate]
        从 [CUS_Contact] 作为 [t33]
        左外连接 [CUS_Phone_JCT] AS [t34] ON [t33].[id] = [t34].[Contact_Id]
        哪里 [t2].[id] = [t33].[id]
        ) 作为 [t35]
    ) AS [日期更新], (
    选择 [t38].[usr_idcreate]
    从 (
        选择顶部 (1) [t36].[usr_idcreate]
        从 [CUS_Contact] 作为 [t36]
        左外连接 [CUS_Phone_JCT] AS [t37] ON [t36].[id] = [t37].[Contact_Id]
        哪里 [t2].[id] = [t36].[id]
        ) 作为 [t38]
    ) AS [Usr_idcreate], (
    选择 [t41].[usr_idupdate]
    从 (
        选择顶部 (1) [t39].[usr_idupdate]
        从 [CUS_Contact] 作为 [t39]
        左外连接 [CUS_Phone_JCT] AS [t40] ON [t39].[id] = [t40].[Contact_Id]
        哪里 [t2].[id] = [t39].[id]
        ) 作为 [t41]
    ) AS [Usr_idupdate], [t2].[id] AS [id2]
从 (
    选择 [t0].[id]
    从 [CUS_Contact] 作为 [t0]
    左外连接 [CUS_Phone_JCT] AS [t1] ON [t0].[id] = [t1].[Contact_Id]
    按 [t0].[id] 分组
    ) 作为 [t2]
在哪里 ((
    选择 [t5].[Cus_Id]
    从 (
        选择顶部 (1) [t3].[Cus_Id]
        从 [CUS_Contact] 作为 [t3]
        左外连接 [CUS_Phone_JCT] AS [t4] ON [t3].[id] = [t4].[Contact_Id]
        哪里 [t2].[id] = [t3].[id]
        ) 作为 [t5]
    )) = @p0
走

-- 区域参数
声明 @x1 内部设置 @x1 = 9327
-- 结束区域
选择 [t2].[电话] 作为 [值]
从 [CUS_Contact] 作为 [t0]
左外连接 [CUS_Phone_JCT] AS [t1] ON [t0].[id] = [t1].[Contact_Id]
左外连接 [CUS_Phone] AS [t2] ON [t2].[id] = [t1].[Phone_Id]
在哪里@x1 = [t0].[id]
走
-- 区域参数
声明 @x1 内部设置 @x1 = 9328
-- 结束区域
选择 [t2].[电话] 作为 [值]
从 [CUS_Contact] 作为 [t0]
左外连接 [CUS_Phone_JCT] AS [t1] ON [t0].[id] = [t1].[Contact_Id]
左外连接 [CUS_Phone] AS [t2] ON [t2].[id] = [t1].[Phone_Id]
在哪里@x1 = [t0].[id]

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    除了joinCUS_Phone 之外,我对你使用cp3.Phone.Phone 有点困惑,所以我假设前者意味着你不需要后者。否则,只需在join 中将p3 切换为cp3 并相应地调整g.Select()

    也就是说,您应该能够简单地根据联系人 ID 进行分组:

    var q = from c in CUS_Contact
            join cp in CUS_Phone_JCT on c.Id equals cp.Contact_Id into cp2 
            from cp3 in cp2.DefaultIfEmpty()
            group new { c, cp3.Phone.Phone } by c.Id into g
            let c = g.First().c
            select new {
                           c.Id,
                           c.Cus_Id, 
                           c.Namefirst, 
                           c.Namemiddle, 
                           c.Namelast, 
                           Phones = g.Select(x => x.Phone)
                           c.Title, 
                           c.Dept, 
                           c.Des, c.Datecreate, 
                           c.Dateupdate, 
                           c.Usr_idcreate, 
                           c.Usr_idupdate
                       };
    
    foreach(var v in q) {
        Console.WriteLine(v.Id + "-" + v.Namefirst);
        foreach(var p in v.Phones) {
            Console.WriteLine(" -" + p);
        }
    }
    

    在黑暗中拍摄几张照片以提高性能:

    var q = from c in CUS_Contact
            join cp in CUS_Phone_JCT on c.Id equals cp.Contact_Id into cp2 
            from cp3 in cp2.DefaultIfEmpty()
            group new { c, cp3.Phone.Phone } by c.Id into g
            let c = g.First().c
            select new {
                           c.Id,
                           c.Cus_Id, 
                           c.Namefirst, 
                           c.Namemiddle, 
                           c.Namelast, 
                           Phones = g.Select(x => x.Phone)
                           c.Title, 
                           c.Dept, 
                           c.Des, c.Datecreate, 
                           c.Dateupdate, 
                           c.Usr_idcreate, 
                           c.Usr_idupdate
                       };
    

    您也可以尝试按复合键(包含所有 c 字段)而不是 c.Id 进行分组:

            group cp3.Phone.Phone
              by new { c.Id, c.Cus_Id, c.Namefirst, ETC } into g
            let c = g.Key
            select new {
                           ...
                           Phones = g.Select(p => p),
                           ...
                       }
    

    更新:将复合键示例调整为仅对 Phone 值进行分组,因为您需要的所有其他内容都应该在键中。


    更新 2:您可以通过嵌入子查询来简化一些事情:

    var q = from c in CUS_Contact
            select new {
                           c.Id,
                           c.Cus_Id, 
                           c.Namefirst, 
                           c.Namemiddle, 
                           c.Namelast, 
                           Phones = (from cp in CUS_Phone_JCT
                                     where c.Id == cp.Contact_Id
                                     select cp.Phone.Phone),
                           c.Title, 
                           c.Dept, 
                           c.Des, c.Datecreate, 
                           c.Dateupdate, 
                           c.Usr_idcreate, 
                           c.Usr_idupdate
                       };
    

    【讨论】:

    • 我也不确定Phone.Phone。除非我添加第二个,否则它将输出表名“CUS_Phone”。该查询效果很好,只是它需要将近 4 秒,而之前的查询只需要不到十分之一秒。有什么想法吗?
    • 生成的SQL是什么样的?
    • 这里太大了,不能进入。 600 个字符的限制,大约 4000 个。
    • 您可以使用 SQL 的摘录来更新问题。我很好奇它使用了哪些子查询。
    • 按复合键分组是票!我也在根据你给我的东西自己做一些事情。感谢您的所有帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    • 2017-01-19
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 2013-06-13
    • 2012-10-21
    相关资源
    最近更新 更多