开发者

Limiting the NULL subqueries from result set

The setup is simple, a Master table and a linked Child table (one master, many children). Lets say we want to extract all masters and their top chronological child value (updated, accessed, etc). A query would look like this (for example):

var masters = from m in Master
              let mc = m.Childs.Max(c => c.CreatedOn)
              select new { m, mc };

A potential problem occurs if master has no children, the subquery will yield NULL and conversion from NULL to DateTime will fail with

InvalidOperationException: The null value cannot be assigned to a member with type System.DateTime which is a non-nullable value type.

Solution to exception is to cast mc to DateTime?, but I need masters that have some children and just bypass few which have no children yet.

Solution #1 Add where m.Childs.Count() > 0. This one kicked me hard and unexpected, the generated SQL was just plain awful (as was its execution plan) and ran almost twice as slow:

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

Solution #2 with where mc != null is even worst, it gives a shorter script but it executes far longer than the above one (takes roughly the same time as the two above together)

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

All in all开发者_JAVA技巧 a lot of wasted SQL time to eliminate a few rows from tens or thousands or more. This led me to Solution #3, get everything and eliminate empty ones client side, but to do that I had to kiss IQueryable goodbye:

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

and this works, however I am trying to figure out if there are any downsides to this? Will this behave anyway fundamentally different then with full monty IQueryable? I imagine this also means I cannot (or should not) use masters as a factor in a different IQueryable? Any input/observation/alternative is welcomed.


Just based on this requirement:

a Master table and a linked Child table (one master, many children). Lets say we want to extract all masters and their top chronological child value

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]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜