【发布时间】:2011-02-10 12:58:28
【问题描述】:
我有三个表:UserObjects、UserObjectsRelations、UserClasses UserObjects 和 UserClasses 之间的模型 M:N 关系。
现在我需要选择这个:
All(UserObjects) - Intersect(UserObjectRelations -> UserObjects).Where(UserObjectRelation -> UserClassId IN (some list))
这意味着我有一个 UserClassIds 列表,用于过滤 UserClasses(或直接关系),我需要找到所有未分配给 所有这些 UserClasses 的 UserObjects。
示例:假设我有 UserClassId IN (1,2) 过滤的 UserObjectRelations:
UserClassId | UserObjectId
--------------------------
1 | 1
2 | 1
2 | 2
我还有许多其他用户对象。我的查询结果应该是此结果集中未提及的所有 UserObjects + Id = 2 的 UserObject,因为它与所有请求的 UserClasses 无关。
问题是 SQL 查询是由实体框架生成的(我们无法完全控制生成的 SQL),所以我们最初使用 INTERSECT 的方法失败了 - 对于许多 UserClasses,它会创建过于复杂的查询,有时 SQL Server 会引发错误因为嵌套很深。
它创建这样的查询(但非常庞大,因为 EF 不使用 * 表示法并且它真的很喜欢很多嵌套的 SELECT):
SELECT Unsused.*
FROM dbo.UserObjects AS Unsused
WHERE Unsused.IsDeleted = 0
EXCEPT (
SELECT U.*
FROM dbo.UserObjects AS U
INNER JOIN dbo.UserObjectRelations AS UR ON UR.UserObjectId = U.Id
WHERE UR.UserClassId = 1
INTERSECT (
SELECT U.*
FROM dbo.UserObjects AS U
INNER JOIN dbo.UserObjectRelations AS UR ON UR.UserObjectId = U.Id
WHERE UR.UserClassId = 2
))
我现在正在重写查询 - 首先在 SQL 中,稍后我将尝试在 Linq-To-Entities 中定义它。我想出了这个:
SELECT Unused.*
FROM dbo.UserObjects AS Unused
LEFT JOIN (
SELECT UsageReport.Id
FROM (
SELECT Tmp.Id, COUNT(*) AS Usage
FROM dbo.UserObjects AS Tmp
INNER JOIN dbo.UserObjectRelations AS DefiningRelations ON
Tmp.Id = DefiningRelations.UserObjectId
WHERE DefiningRelations.UserClassId IN (1, 2)
GROUP BY Tmp.Id) AS UsageReport
WHERE UsageReport.Usage = 2
) AS Used ON Used.Id = Unused.Id
WHERE Unused.IsDeleted = 0 AND Used.Id IS NULL
查询可能看起来不太好,但我已经在尝试避免我不知道如何转换为 Linq-To-Entities 的构造。
我仍然对查询不满意。我不喜欢这部分:WHERE UsageReport.Usage = 2 将内部选择过滤为仅由两个用户类使用的用户对象。此参数必须是动态的,并且始终表示传入 IN 子句的 Id 数量。
你知道如何更好地编写这样的查询吗?
【问题讨论】:
-
在您的查询中,您根本没有使用您的
LEFT JOIN,我认为您缺少过滤器。也许Used.Id IS NULL? -
@Lamak:你说得对,我已经修改了我的查询。谢谢。
标签: sql sql-server tsql entity-framework linq-to-entities