drewob
drewob

Reputation: 507

Linq making very inefficient Entity Framework query

Entity Framework generates very poorly performing SQL for the following LINQ query:

var query = _context.Sessions
                    .Where(s => s.OrganizationId == orgId && s.Device != null && s.Device.User != null)
                    .Select(s => s.Device.User)
                    .Distinct();

Generates this SQL:

exec sp_executesql N'SELECT
[Distinct1].[Id] AS [Id], 
[Distinct1].[Email] AS [Email], 
[Distinct1].[Sex] AS [Sex], 
[Distinct1].[Age] AS [Age]
FROM ( SELECT DISTINCT 
    [Extent4].[Id] AS [Id], 
    [Extent4].[Email] AS [Email], 
    [Extent4].[Sex] AS [Sex], 
    [Extent4].[Age] AS [Age]
    FROM   (SELECT [Extent1].[OrganizationId] AS [OrganizationId], [Extent3].[UserId] AS [UserId1]
        FROM   [dbo].[Sessions] AS [Extent1]
        INNER JOIN [dbo].[Devices] AS [Extent2] ON [Extent1].[DeviceId] = [Extent2].[Id]
        LEFT OUTER JOIN [dbo].[Devices] AS [Extent3] ON [Extent1].[DeviceId] = [Extent3].[Id]
        WHERE [Extent2].[UserId] IS NOT NULL ) AS [Filter1]
    LEFT OUTER JOIN [dbo].[Users] AS [Extent4] ON [Filter1].[UserId1] = [Extent4].[Id]
    WHERE [Filter1].[OrganizationId] = @p__linq__0
)  AS [Distinct1]',N'@p__linq__0 int',@p__linq__0=2

The SQL I'm actually looking to execute is the following, which runs lightning fast:

select distinct u.*
from Sessions s
inner join Devices d on s.DeviceId = d.Id
inner join Users u on d.UserId = u.Id
where OrganizationId = 2

How can I get the Entity Framework-generated SQL to be as close to this query as possible?

Upvotes: 2

Views: 552

Answers (4)

J4ime
J4ime

Reputation: 155

I dont like use the DISTINCT , if a query contains it then the query's wrong.

Other way to do it

var query = _context.Sessions.Include("Device.User.Email")
                    .Where(s => s.OrganizationId == orgId);

Upvotes: 0

Rob
Rob

Reputation: 27357

You can do it pretty simply:

_context.Sessions
    .Where(s => s.OrganizationId == 2)
    .Select(s => s.Device.User)
    .Distinct();

You do not need to check for null, as it will perform an INNER JOIN for you.

Upvotes: 0

André Kops
André Kops

Reputation: 2713

Try starting with the users table:

var query = (
    from u in _context.Users
    where u.Devices.Any(d => d.Sessions
        .Any(s => s.OrganisationId == orgId)
    )
    select u
);

It won't do the query you specified but what it does return might have the same good performance.

Upvotes: 3

Yacoub Massad
Yacoub Massad

Reputation: 27861

Why select the whole User entity if you just want the email?

Try this:

var query = _context.Sessions
                    .Where(s => s.OrganizationId == orgId && s.Device != null && s.Device.User != null)
                    .Select(s => s.Device.User.Email)
                    .Distinct();

Upvotes: 4

Related Questions