How to apply self join in Linq Query?

Viewed 190

Books Table

Id  VendorId   ASIN   Price
--  --------   ----   ------
 1   gold123    123     10
 2   sil123     123     11
 3   gold456    456     15
 4   gold678    678     12
 5   sil456     456     12
 6   gold980    980     12

I want to write a linq query which will return me rows for which corresponding to every gold if sil vendor id not exist. The last three digit of vendor Id is corresponding ASIN column in that row.

Ex- For gold123 corresponding sil123 exist so that row will not be returned but for gold678 and gold980 corresponding sil not exist. So those rows will be returned.

I tried following

     var gold = _repository.Query<Books>().Where(x => 
                  x.VendorId.Contains("gold"))
                 .OrderBy(x => x.Id).Skip(0).Take(500).ToList();

    var asinsForGold = gold.Select(x => x.ASIN).ToList();



  var correspondingSilver = _repository.Query<Books>().Where(x => 
                            x.VendorId.Contains("sil") 
                           && asinsForGold.Contains(x.ASIN)).ToList();

var correspondingSilverAsins = correspondingSilver.Select(x => x.ASIN).ToList();

var goldWithoutCorrespondingSilver = gold.Where(x => 
                                !correspondingSilverAsins.Contains(x.ASIN));

Can We apply self join or better way to get result only in one query instead of two query and several other list statement.

2 Answers

It's just another predicate, "where a corresponding silver vendor doesn't exist":

var goldWoSilver = _repository.Query<Books>()
    .Where(x => x.VendorId.Contains("gold"))
    .Where(x => !_repository.Query<Books>()
        .Any(s => s.ASIN == x.ASIN
               && s.VendorId.Contains("sil"))
    .OrderBy(x => x.Id).Skip(0).Take(500).ToList();

In many cases this is a successful recipe: start the query with the entity you want to return and only add predicates. In general, joins shouldn't be used for filtering, only to collect related data, although in that case navigation properties should be used which implicitly translate to SQL joins.

See if it helps -

var goldWithoutCorrespondingSilver = from b1 in books
                                  join b2 in books on b1.ASIN equals b2.ASIN
                                  where b1.VendorId.Contains("gold")
                                  group b2 by b1.VendorId into g
                                  where !g.Any(x => x.VendorId.Contains("sil"))
                                  select g.FirstOrDefault();

What I have done is -

  • Selected records with matching ASIN
  • Grouped them by VendorID
  • Selected ones which do not have sil
Related