如何正确分组我的 LINQ 查询?
Posted
技术标签:
【中文标题】如何正确分组我的 LINQ 查询?【英文标题】:How do I group my LINQ query correctly? 【发布时间】:2013-12-11 12:43:03 【问题描述】:我在输出我想要的 LINQ 查询时遇到问题。我不确定我是否采取了正确的方法。
表格:
我有两个表Contacts
和Permissions
,我用它们执行LEFT OUTER JOIN。
加入查询:
from contact in Contacts
join permission in Permissions on contact.Id equals permission.ObjectId into permissionGrp
from p in permissionGrp.DefaultIfEmpty()
where (p==null && isAllowed) || (p!=null && /* ... further conditions */))
orderby /* ... ordering removed */
select new contact, permission = p ;
这会将权限匹配到适用的联系人,如果不存在匹配的权限,则为null
。
希望
我不希望有重复的联系人,我只对第一个 Contact-Permission 记录感兴趣。像这样:
尝试:
所以我假设我需要Group By
我的contact.Id
并以某种方式在权限集合上选择FirstOrDefault()
。
from contact in Contacts
join permission in Permissions on contact.Id equals permission.ObjectId into permissionGrp
from p in permissionGrp.DefaultIfEmpty()
where (p==null && isAllowed) || (p!=null && /* ... further conditions */))
orderby /* ... ordering removed */
group p by contact into contactPermissionsGrp
select new contact = contactPermissionsGrp.Key, permission = contactPermissions.FirstOrDefault() ;
结果:
不幸的是,这会导致NotSupportedException: Specific method is not supported.
。但我不确定我的方法是正确的还是LightSpeed ORM 的限制。
任何建议将不胜感激。
【问题讨论】:
【参考方案1】:用更多问题回答问题
是否可以在目标数据库中使用原始 SQL 进行查询?如果答案是否定的,那么您有什么希望在 LINQ 中复制它?我个人认为通过 LINQ 针对 mysql 实现的目标是不可能的。可以在原始 T-SQL 中的 MS SQL 中定义一个表达式并将 RANK() 列附加到它,然后对该表达式进行查询。
我觉得您可用的解决方案是:
了解如何在您的本地方言中使用原始 SQL 编写此查询。 Lightspeed 将允许您执行原始 SQL,它甚至会(在您返回足够多的列的情况下)将该自定义查询重新水化为实体(但在这种情况下,我认为这不是您所追求的)。
放弃有效减少数据库中的“重复”。将重复项拉入内存,然后使用 LINQ 查询针对您返回的 IEnumerable 集在内存中减少它们。
更改您的数据库架构,以便您可以进行更简单的查询。有时在这样的情况下,我会很荣幸在 Contact 表中有一个列,例如“MostSignificantPermssion”。这有很多优点:
获取联系人和 ONE 重要权限记录的查询更简单。 让其他开发人员更清楚地认识到其中一项权限具有特殊意义。选项!
附录-MS SQL中实现(1)的示例
WITH LastUsagePerPerson AS (
SELECT
ULE.PersonId,
ULE.[Device],
ULE.[AppVersion],
ULE.[CreatedOn],
ROW_NUMBER() OVER(PARTITION BY ULE.PersonId ORDER BY ULE.CreatedOn DESC) AS rk
FROM [dbo].[UsageLogEntry] ULE
)
SELECT
[FirstName]
,[LastName]
,[EmailAddress]
,[EmailAddressUnverified]
,[MobileNumber]
,[MobileNumberUnverified]
,[LastDeviceUsed] = LastUsagePerPerson.Device
,[LastAppVersion] = LastUsagePerPerson.AppVersion
,[LastDeviceUsage] = LastUsagePerPerson.CreatedOn
,[LastLoggedInOn]
FROM [dbo].[Person] P
LEFT JOIN LastUsagePerPerson ON P.Id = LastUsagePerPerson.PersonId
WHERE rk = 1
ORDER BY [Id]
【讨论】:
谢谢杰森,这是个好建议。我会考虑 1 和 2 并让你知道结果。我认为“MostSignificantPermission”不会起作用,因为权限的重要性会根据用户使用系统的多个上下文而发生很大变化。 是的,我没想到这对您的情况有意义。在某些情况下确实如此。 IE。在我当前的项目中,我为 Person 的 LastSuccessfulPayment 和他们的 MostFrequentMerchant 维护一个 FK。虽然将这些作为 FK 维护需要做额外的工作,但它使系统整体更高效,并减少了对一些海量索引的需求。【参考方案2】:我已经理解你想要得到什么并且我已经解决了你的问题,只需按照下面的代码我所做的......
select * from contacts as a
left join permissions as b
on a.ContactId = b.ContactId
group by a.ContactId ;
我已经使用上面的代码得到了你想要得到的结果。试试吧,你的问题会解决的。
【讨论】:
这个问题是关于 LINQ 而不是 SQL。你还没有“理解”我想要做什么,我的问题远比简单的 SQL 连接复杂。 同意@Scott,这个答案完全忽略了这个问题。【参考方案3】:我不知道 Lightspeed 能做什么或不能做什么。尝试简化您的 LINQ 查询 在 EF 我会做这样的事情。
from c in Contacts
let p = (from p in permission where p.ObjectId == c.Id select p).FirstOrDefault()
select new ContactID = c.Id,
Name = c.Name,
Permission = p.PermissionId,
Permitted = p.Permitted;
【讨论】:
我可以看到您来自哪里,LightSpeed ORM 确实支持这一点,但它会为此查询每个联系人记录。这非常低效,这就是我使用 LEFT OUTER JOIN 的原因。 EF 足够聪明,可以将其优化为单个 sql(它在联系人和权限之间使用 OUTER APPLY),而且可能 Lightspeed 也同样聪明。 LightSpeed 没有转化为任何 OUTER APPLY,因为 MySQL 不支持它。所以搬到EF会有同样的问题。我正在尝试设置 nHibernate 以查看它是否支持我的查询。 我用实体框架尝试了你的方法,我得到了一个NotSupportedException
和消息Unable to create a constant value of type 'eftest.Permission'. Only primitive types or enumeration types are supported in this context
您使用 p 变量的目的是否超出了我的示例中所示的范围?如图所示,没有什么需要常量值。以上是关于如何正确分组我的 LINQ 查询?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 LINQ 和 Lambda 表达式 LINQ C# 上正确执行 SQL 查询
为啥 LINQ 在我的查询中使用错误的数据类型,而它在 EF 架构中被正确声明?