设置很简单,主表和链接的子表(一个主人,很多孩子)。让我们说我们想要提取所有主人及其最重要的时间顺序子值(更新,访问等)。查询看起来像这样(例如):
var masters = from m in Master
let mc = m.Childs.Max(c => c.CreatedOn)
select new { m, mc };
如果master没有子节点,则会出现潜在问题,子查询将产生NULL,并且从NULL到DateTime的转换将失败
InvalidOperationException:null 值无法分配给成员 类型为System.DateTime,它是一个 非可空值类型。
异常的解决方案是将mc转换为DateTime?
,但我需要拥有一些孩子的主人,并且绕过几个没有孩子的主人。
解决方案#1 添加where m.Childs.Count() > 0
。
这个让我很难和意外,生成的SQL只是非常糟糕(就像它的执行计划一样)并且运行速度差不多两倍:
SELECT [t2].[Name] AS [MasterName], [t2].[value] AS [cm]
FROM (
SELECT [t0].[id], [t0].[Name], (
SELECT MAX([t1].[CreatedOn])
FROM [Child] AS [t1]
WHERE [t1].[masterId] = [t0].[id]
) AS [value]
FROM [Master] AS [t0]
) AS [t2]
WHERE ((
SELECT COUNT(*)
FROM [Child] AS [t3]
WHERE [t3].[masterId] = [t2].[id]
)) > @p0
带有where mc != null
的解决方案#2 甚至更糟,它提供了一个更短的脚本,但执行时间远远超过上面的一个(与上面的两个大致相同)< / p>
SELECT [t2].[Name] AS [MasterName], [t2].[value] AS [cm]
FROM (
SELECT [t0].[id], [t0].[Name], (
SELECT MAX([t1].[CreatedOn])
FROM [Child] AS [t1]
WHERE [t1].[masterId] = [t0].[id]
) AS [value]
FROM [Master] AS [t0]
) AS [t2]
WHERE ([t2].[value]) IS NOT NULL
总而言之,浪费了大量SQL时间来消除数十或数千或更多的行。这导致我解决方案#3 ,得到一切并消除空客户端,但要做到这一点,我不得不亲吻IQueryable再见:
var masters = from m in Master
let mc = (DateTime?)m.Childs.Max(c => c.CreatedOn)
select new { m, mc };
var mastersNotNull = masters.AsEnumerable().Where(m => m.mc != null);
这是有效的,但我想弄清楚这是否有任何缺点?这种表现方式与完全monty IQueryable完全不同吗?我想这也意味着我不能(或不应该)使用大师作为不同IQueryable的一个因素?欢迎任何输入/观察/替代。
答案 0 :(得分:0)
仅基于此要求:
主表和链接的子表 桌子(一个主人,很多孩子)。 让我们说我们想要提取所有 大师和他们的顶级时间顺序 儿童价值
SELECT [m].[Name] AS [MasterName]
, Max([c].[value]) as [cm]
FROM [Master] AS [m]
left outer join [Child] as [c] on m.id = c.id
group by [m].[name]