限制结果集中的NULL子查询

时间:2011-02-17 14:34:50

标签: c# linq linq-to-sql

设置很简单,主表和链接的子表(一个主人,很多孩子)。让我们说我们想要提取所有主人及其最重要的时间顺序子值(更新,访问等)。查询看起来像这样(例如):

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的一个因素?欢迎任何输入/观察/替代。

1 个答案:

答案 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]