Retrieve single record from child table in join

Posts   
 
    
jg
User
Posts: 25
Joined: 29-Dec-2011
# Posted on: 09-Jan-2013 22:58:25   

SQL Server 2008 .Net 4.0 C# LLBLGen version 3.1 Final

Here is a simplified version of my problem.

I have two tables (Waiver and WaiverWorkflowLog). The WaiverWorkflowLog table will contain several records for each Waiver. I need to retrieve all the waivers with the most recent WaiverWorkflowLog entry for that Waiver.

This is essentially the SQL I need to generate:

select *
from Waiver w
join WaiverWorkflowLog wwl on 
wwl.WaiverID = w.WaiverID 
and wwl.WaiverWorkflowLogID = (select max(WaiverWorkflowLogID) from WaiverWorkflowLog wm where wm.WaiverID = w.WaiverID)

I found some documentation on FieldCompareSetPredicate that looks like it might work, but I'm not sure how or if I'm even going about this properly.

Can you point me in the right direction?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 10-Jan-2013 06:35:05   

FieldCompareSetPredicate is more for situations like:

WHERE OrderId IN (SELECT ...)

For what you want is more appropriate a ScalarQueryExpression and use it to filter the related collection (prefetchPath).

Also, to fetch the related collection you should use PrefetchPaths. You can filter the prefetchPath to include only the MAX related entity.

Example: I want to fetch all customers, and for each one I want to fetch the latest order, where "latest order" is max(orderId). The code would look like this:

// define the scalar field and filter
var orderIdMax = new EntityField2("MaxOrderFromCust",
    new ScalarQueryExpression(OrderFields.OrderId.SetObjectAlias("O2")
        .SetAggregateFunction(AggregateFunction.Max),
        (OrderFields.CustomerId.SetObjectAlias("O2") == OrderFields.CustomerId)));
            
// path 
var path = new PrefetchPath2((int)EntityType.CustomerEntity);
path.Add(CustomerEntity.PrefetchPathOrders, 0, new PredicateExpression(OrderFields.OrderId == orderIdMax));

// fetch
var customers = new EntityCollection<CustomerEntity>();
using (var adapter = new DataAccessAdapter())
{
    adapter.FetchEntityCollection(customers, null, path);
}

The generated SQL, due to the PrefetchPath, is done in two queries:

SELECT [Northwind].[dbo].[Customers].[Address],
       [Northwind].[dbo].[Customers].[City],
       ...
FROM   [Northwind].[dbo].[Customers] 


SELECT [Northwind].[dbo].[Orders].[CustomerID] AS [CustomerId],
       [Northwind].[dbo].[Orders].[EmployeeID] AS [EmployeeId],
       [Northwind].[dbo].[Orders].[Freight],
       ...
FROM   [Northwind].[dbo].[Orders]
WHERE  ([Northwind].[dbo].[Orders].[CustomerID] IN
        (SELECT [Northwind].[dbo].[Customers].[CustomerID] AS [CustomerId]
         FROM   [Northwind].[dbo].[Customers])
        AND (([Northwind].[dbo].[Orders].[OrderID] =
              (SELECT MAX([O2].[OrderID]) AS [OrderId]
               FROM   [Northwind].[dbo].[Orders] [O2]
               WHERE  ([O2].[CustomerID] = [Northwind].[dbo].[Orders].[CustomerID]))))) 
David Elizondo | LLBLGen Support Team
jg
User
Posts: 25
Joined: 29-Dec-2011
# Posted on: 10-Jan-2013 23:26:26   

Thank You! That got me headed in the right direction.

I was actually trying to build a result set instead of just grabbing the entities, so I had to modify the code you provided a bit.

I'll post it here in case someone else is attempting the same thing:


EntityField2 WWLMax = new EntityField2("MaxLogForWaiver",
    new ScalarQueryExpression(WaiverWorkflowLogFields.WaiverWorkflowLogID.SetObjectAlias("WWL")
        .SetAggregateFunction(AggregateFunction.Max),
        (WaiverWorkflowLogFields.WaiverID.SetObjectAlias("WWL") == WaiverWorkflowLogFields.WaiverID)));
IEntityRelation WWLRelation = bucket.Relations.Add(WaiverEntity.Relations.WaiverWorkflowLogEntityUsingWaiverID, JoinHint.Left);
WWLRelation.CustomFilter = new PredicateExpression(WaiverWorkflowLogFields.WaiverWorkflowLogID == WWLMax);

That worked perfectly for me. Thanks again for the help!