2015-02-19 6 views
1

У меня есть набор пользователей и посещений. (Так пользователь посещает)Linq left join with group join

Посещение имеет свойство навигации пользователя.

Мне нужно найти пользователей, которые не посещают.

Я могу сделать это, найдя пользователей, которые посещают, находя всех пользователей, а затем принимая во внимание.

Я пытался найти решение, которое выполняется быстрее.

Это то, что я прямо сейчас:

var users = _db.Users.AsNoTracking().Include(c => c.City).Where(x => x.City.Id == city); 

    var groupedUsers = _db.Visits.AsNoTracking().Include(c => c.City).Include(a=>a.VisitedBy).Where(x => x.City.Id == city).GroupBy(x => x.VisitedBy).Select(group => new { VisitedBy = group.Key, Count = group.Count() }); 

    var visitingUsers = groupedUsers.Select(user => user.VisitedBy); 

    var dif = users.Except(visitingUsers); 

Однако я пытался GroupJoin, как показано ниже:

var results = _db.Users.Include(c => c.City).Where(c => c.City.Id == city). 
      GroupJoin(_db.Visits.Include(c => c.City).Include(u => u.VisitedBy), u => u.Id, v => v.VisitedBy.Id, (u, v) => new { User = u , Visits = v }) 
      .Select(o=>o.User); 

Но это дает мне все пользователи, я хочу, чтобы пользователи, которые надевают» t существует в наборе посещений.

Любая помощь?

+0

Нулевой чек в сочетании с LEFT JOIN обычно выполняет то, что вы пытаетесь ... – Didaxis

+0

где? Не знаю, где я должен поставить нулевую проверку. – DarthVader

+0

В SQL мы делаем: SELECT ... FROM LEFT JOIN b ON (a.X = b.X) WHERE b.X IS NULL'. Нет ли сопоставимого метода Linq для этого? – Didaxis

ответ

3

Возможно, вам удастся избежать коррелированного подзапроса в другом ответе, фактически выполнив левое соединение с нулевой проверкой. Вот простой пример:

var A = new [] { new Foo { Bar = 1 }, new Foo { Bar = 2 }}; 
var B = new [] { new Foo { Bar = 2 }}; 

var C = from x in A 
     join y in B on x.Bar equals y.Bar into z 
     from y in z.DefaultIfEmpty() 
     where y == null 
     select x; 

Проверить излучаемый SQL ...

+0

Подзапрос не должен быть медленнее, чем соединение, но да, это избавится от него. –

+0

@Cory, да, вероятно, это не имеет значения для небольшого количества записей, но мои лучшие практики SQL всегда говорят мне, что я получаю присоединение к подзапросам, если вы не можете делать предположения о том, как индексируются данные. – Didaxis

+0

Чтобы сделать это быстрее, что я должен индексировать? – DarthVader

3

Я не слишком уверен, что если фильтрация город, что вы после однако следующие должны добиться того, что вы хотите:

var visitsToCity = _db.Visits.Where(v => v.City.Id == city); 
var usersOfCity = _db.Users.Where(u => u.City.Id == city); 

var nonVisitingUsers = usersOfCity.Where(u => !visitsToCity.Any(v => v.VisitedBy == u)); 

Последние Where в сочетании с Any должно привести к SQL заявления, как:

SELECT * FROM Users u WHERE u.CityId = @p0 AND 
    NOT EXISTS(SELECT 1 FROM Visits v WHERE v.CityId = @p0 AND 
        v.VisitedById = u.Id) 

Где @p0 будет поставляться со значением city.