LINQ to SQL LEFT JOIN,GROUP BY,COUNT,MAX和子查询

时间:2018-04-18 14:20:43

标签: c# sql-server linq

我有3个表:用户,角色,投标 其中两个是由ASP.NET Identity创建的,Bids是我的自定义表(它是Users表的次要表)

我正在尝试让所有非特定角色的用户(所有非管理员用户)显示出价数量(可以为空 - 我已离开加入),显示每个用户的最高出价并显示出价最高

SQL查询很简单:

SELECT
    U.Id
   ,U.UserName
   ,U.Email
   ,ISNULL(MAX(B.PriceInPLN), 0)
   ,COUNT(B.BidID)
   ,IIF(MAX(B.PriceInPLN)=(SELECT TOP 1 PriceInPLN FROM Bids B2 (NOLOCK) ORDER BY PriceInPLN DESC),1,0 ) AS Max
FROM
    AspNetUsers U ( NOLOCK )
    JOIN AspNetUserRoles UR ( NOLOCK ) ON U.Id = UR.UserId
    LEFT JOIN Bids B ( NOLOCK ) ON B.ApplicationUserId = U.Id
WHERE
    UR.RoleId != '9508f9d2-12fb-4175-89a7-3275cb7616ae'
GROUP BY
    U.Id
   ,U.UserName
   ,U.Email

但是我无法创建正确的LINQ查询。以下是我到目前为止的情况:

var users =(
from u in db.Users
where !u.Roles.Select(r => r.RoleId).Contains(adminRoleId)//everyone except admins
join b in db.Bids on u equals b.ApplicationUser into ub
from subset in ub.DefaultIfEmpty()//LEFT JOIN
group subset by new { u.Id, u.UserName, u.Email, u.EmailConfirmed, u.Online } into grouped
select new UserReturnModel
{
    Id = grouped.Key.Id,
    Name = grouped.Key.UserName,
    Email = grouped.Key.Email,
    EmailConfirmed = grouped.Key.EmailConfirmed,
    Online = grouped.Key.Online,
    BidsCount = grouped.Count(c => c.ApplicationUserId == grouped.Key.Id),
    PriceInPLN = grouped.Max(c => c.PriceInPLN),
    IsMax = (grouped.Max(c=>c.PriceInPLN) == db.Bids.Max(b=>b.PriceInPLN))
}).ToList();

我无法让Max列正常工作 - 它总是说真的 以下是列定义,可以轻松显示我想要的内容:

  • Id =用户ID,
  • 名称=用户名,
  • 电子邮件=用户电子邮件,
  • BidsCount =该用户创建的出价数
  • PriceInPLN =该用户的最高价格
  • 与其他优惠相比,IsMax =此用户优惠最高,如果没有优惠,则该优惠应为假

我在ASP.NET中使用它,在调试时我注意到LINQ创建的SQL查询很大:

SELECT 
    1 AS [C1], 
    [Project6].[Id] AS [Id], 
    [Project6].[UserName] AS [UserName], 
    [Project6].[Email] AS [Email], 
    [Project6].[EmailConfirmed] AS [EmailConfirmed], 
    [Project6].[Online] AS [Online], 
    [Project6].[C4] AS [C2], 
    [Project6].[C1] AS [C3], 
    CASE WHEN (([Project6].[C2] = [GroupBy3].[A1]) OR (([Project6].[C2] IS NULL) AND ([GroupBy3].[A1] IS NULL))) THEN cast(1 as bit) WHEN ( NOT (([Project6].[C3] = [GroupBy4].[A1]) AND ((CASE WHEN ([Project6].[C3] IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END) = (CASE WHEN ([GroupBy4].[A1] IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END)))) THEN cast(0 as bit) END AS [C4]
    FROM    (SELECT 
        [Project3].[C1] AS [C1], 
        [Project3].[C2] AS [C2], 
        [Project3].[C3] AS [C3], 
        [Project3].[Id] AS [Id], 
        [Project3].[Online] AS [Online], 
        [Project3].[Email] AS [Email], 
        [Project3].[EmailConfirmed] AS [EmailConfirmed], 
        [Project3].[UserName] AS [UserName], 
        (SELECT 
            COUNT(1) AS [A1]
            FROM ( SELECT 
                [Extent4].[Id] AS [Id], 
                [Extent4].[Online] AS [Online], 
                [Extent4].[Email] AS [Email], 
                [Extent4].[EmailConfirmed] AS [EmailConfirmed], 
                [Extent4].[UserName] AS [UserName], 
                [Extent5].[ApplicationUserId] AS [ApplicationUserId]
                FROM  [dbo].[AspNetUsers] AS [Extent4]
                INNER JOIN [dbo].[Bids] AS [Extent5] ON [Extent5].[ApplicationUserId] = [Extent4].[Id]
                WHERE ( NOT EXISTS (SELECT 
                    1 AS [C1]
                    FROM [dbo].[AspNetUserRoles] AS [Extent6]
                    WHERE ([Extent4].[Id] = [Extent6].[UserId]) AND ([Extent6].[RoleId] = @p__linq__0)
                )) AND ([Project3].[Id] = [Extent4].[Id]) AND ([Project3].[UserName] = [Extent4].[UserName]) AND (([Project3].[Email] = [Extent4].[Email]) OR (([Project3].[Email] IS NULL) AND ([Extent4].[Email] IS NULL))) AND ([Project3].[EmailConfirmed] = [Extent4].[EmailConfirmed]) AND ([Project3].[Online] = [Extent4].[Online]) AND ([Extent5].[ApplicationUserId] = [Project3].[Id])
            )  AS [Project5]) AS [C4]
        FROM ( SELECT 
            [GroupBy1].[A1] AS [C1], 
            [GroupBy1].[A2] AS [C2], 
            [GroupBy1].[A3] AS [C3], 
            [GroupBy1].[K1] AS [Id], 
            [GroupBy1].[K2] AS [Online], 
            [GroupBy1].[K3] AS [Email], 
            [GroupBy1].[K4] AS [EmailConfirmed], 
            [GroupBy1].[K5] AS [UserName]
            FROM ( SELECT 
                [Project2].[Id] AS [K1], 
                [Project2].[Online] AS [K2], 
                [Project2].[Email] AS [K3], 
                [Project2].[EmailConfirmed] AS [K4], 
                [Project2].[UserName] AS [K5], 
                MAX([Project2].[PriceInPLN]) AS [A1], 
                MAX([Project2].[PriceInPLN]) AS [A2], 
                MAX([Project2].[PriceInPLN]) AS [A3]
                FROM ( SELECT 
                    [Extent1].[Id] AS [Id], 
                    [Extent1].[Online] AS [Online], 
                    [Extent1].[Email] AS [Email], 
                    [Extent1].[EmailConfirmed] AS [EmailConfirmed], 
                    [Extent1].[UserName] AS [UserName], 
                    [Extent2].[PriceInPLN] AS [PriceInPLN], 
                    [Extent2].[ApplicationUserId] AS [ApplicationUserId]
                    FROM  [dbo].[AspNetUsers] AS [Extent1]
                    LEFT OUTER JOIN [dbo].[Bids] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ApplicationUserId]
                    WHERE  NOT EXISTS (SELECT 
                        1 AS [C1]
                        FROM [dbo].[AspNetUserRoles] AS [Extent3]
                        WHERE ([Extent1].[Id] = [Extent3].[UserId]) AND ([Extent3].[RoleId] = @p__linq__0)
                    )
                )  AS [Project2]
                GROUP BY [Project2].[Id], [Project2].[Online], [Project2].[Email], [Project2].[EmailConfirmed], [Project2].[UserName]
            )  AS [GroupBy1]
        )  AS [Project3] ) AS [Project6]
    CROSS JOIN  (SELECT 
        MAX([Extent7].[PriceInPLN]) AS [A1]
        FROM [dbo].[Bids] AS [Extent7] ) AS [GroupBy3]
    CROSS JOIN  (SELECT 
        MAX([Extent8].[PriceInPLN]) AS [A1]
        FROM [dbo].[Bids] AS [Extent8] ) AS [GroupBy4]

这可以简化吗?

以下是Entity Framework Power Tools生成的我的数据库模型:

enter image description here

1 个答案:

答案 0 :(得分:2)

我不确定为什么你从原始的SQL查询中修改了很多LINQ查询,但是使用我SQL to LINQ Recipe中的规则将SQL查询更接近字面意思

var MaxPriceInPLN = db.Bids.Max(b => b.PriceInPLN);
var ans = from U in db.Users
          where !U.Roles.Any(r => r.RoleId == adminRoleId)
          join b in db.Bids on U.Id equals b.ApplicationUserId into bj
          from b in bj.DefaultIfEmpty()
          group new { U, b } by new { U.Id, U.UserName, U.Email } into Ubg
          let maxBidPriceInPLN = Ubg.Max(Ub => Ub.b.PriceInPLN)
          select new {
              Ubg.Key.Id,
              Ubg.Key.UserName,
              Ubg.Key.Email,
              PriceInPLN = maxBidPriceInPLN ?? 0,
              BidsCount = Ubg.Count(Ub => Ub.b != null),
              IsMax = maxBidPriceInPLN == MaxPriceInPLN
          };

您可以使用EF导航属性来隐藏查询中的联接来简化LINQ查询:

var MaxPriceInPLN = db.Bids.Max(b => b.PriceInPLN);
var ans = from U in db.Users
          where !U.Roles.Any(r => r.RoleId == adminRoleId)
          let maxBidPriceInPLN = U.Bids.Max(b => b.PriceInPLN)
          select new {
              U.Id,
              U.UserName,
              U.Email,
              PriceInPLN = maxBidPriceInPLN ?? 0,
              BidsCount = U.Bids.Count(),
              IsMax = maxBidPriceInPLN == MaxPriceInPLN
          };