简体   繁体   English

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

[英]LINQ to SQL LEFT JOIN, GROUP BY, COUNT, MAX and subquery

I have 3 tables: Users, Roles, Bids. 我有3个表格:用户,角色,出价。
Two of them is created by ASP.NET Identity, Bids is my custom table (it is secondary to Users table) 其中两个是由ASP.NET Identity创建的, Bids是我的自定义表(它是Users表的辅助表)

I'm trying to get all users that aren't in specific role (all non admin users) and show number of bids (they can be null - I have left join), show highest bid per user and show if that bid is max. 我正在尝试获取不是特定角色的所有用户(所有非管理员用户)并显示出价数量(它们可以为null-我已退出加入),显示每个用户的最高出价,并显示该出价是否为最高。

SQL query was quire easy: 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

But I have trouble creating correct LINQ query. 但是我无法创建正确的LINQ查询。 Here is what I have so far: 这是我到目前为止的内容:

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();

I can't get Max column to work correctly - it always says true. 我无法让Max列正常工作-它总是说正确。
Here are column definitions to easily show what I want to get: 以下是列定义,可轻松显示我想要获得的内容:

  • Id = user id, ID =用户ID,
  • Name = user name, 名称=用户名,
  • Email = user email, 电子邮件=用户电子邮件,
  • BidsCount = number of bids created by that user, BidsCount =该用户创建的出价数量,
  • PriceInPLN = max price for that user, PriceInPLN =该用户的最高价格,
  • IsMax = is this user offer highest comparing to other offers, if there are no offers this should be false IsMax =此用户提供的价格是其他提供的最高价格,如果没有提供,则应为false

I'm using that in ASP.NET, when debugging I've noticed that SQL query created by LINQ is large: 我在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]

Can this be simplified? 可以简化吗?

Here is model of my my database generated by Entity Framework Power Tools: 这是由Entity Framework Power Tools生成的我的数据库模型:

在此处输入图片说明

I'm not sure why you modified your LINQ query so much from the original SQL query, but translating the SQL query closer to literally using the rules from my SQL to LINQ Recipe I get 我不确定为什么您要从原始SQL查询中对LINQ查询进行如此多的修改,但是将SQL查询转换为更接近字面意义上的使用从SQL到LINQ Recipe的规则

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
          };

You can simplify the LINQ query by using the EF navigation properties to hide the join from your query: 您可以通过使用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
          };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM