Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I query a junction table

Using Entity Framework/LINQ, I need help with the following.

The database has a table of People with an identity column of PersonId. There is also a Skills table with an identity column of SkillId. These two are connected via a third table PeopleSkills that has its own identity column PeopleSkillsId, a foreign column referencing PersonId and a foreign column referencing SkillId.

The method that I am trying to write is passed a parameter of type List that contains any number of skills that we are looking for. The method should return a List who are linked to all of the skills in the input parameter list. How do I build a list that excludes anyone without all of the skills in the Skills list?

The problem I'm having is that I have very little SQL experience. I do have lots of other programming experience, but SQL is always a bit rough for me. I thought about using a Join, but that's not going to work. i.e. if my person has skills A & B, and the search list has elements for B & C, a join would match them on B and return the person. I need this person to be excluded because he doesn't have both B & C.

I also thought about iterating through the skills list and building a filter, but that seems ugly. This just seems like a problem that LINQ was built to handle, using a List to query another List, and that there should be an elegant solution to.

like image 615
Steve Wash Avatar asked Jul 24 '12 15:07

Steve Wash


People also ask

What is a junction table in SQL?

A junction table contains the primary key columns of the two tables you want to relate. You then create a relationship from the primary key columns of each of those two tables to the matching columns in the junction table. In the pubs database, the titleauthor table is a junction table.

How do I map a one to many relationship in SQL?

To establish a one-to-many relationship, the primary key of table A (the "one" table) must be the secondary key of table B (the "many" table).


1 Answers

I used LinqPad, which uses Linq-to-SQL instead of Linq to Entities, but the concepts should be the same.

First, the data I used to test.

create table People (PersonID int, Name varchar(100))
create table Skills (SkillID int, Skill varchar(100))
create table PeopleSkills (PeopleSkillsID int, PersonID int, SkillID int)

insert People values (1,'Bert'),(2,'Bob'),(3,'Phil'),(4,'Janet')
insert Skills values (1,'C#'),(2,'Linq'),(3,'SQL')
insert PeopleSkills values (1,1,1),(2,1,2),(3,1,3),(4,2,1),(5,2,3),(6,3,2),(7,3,3),(8,4,1),(9,4,2),(10,4,3)

And the solution.

//I don't know how you are specifying your list of skills; for explanatory purposes
//I just generate a list.  So, our test skill set is C#, Linq, and SQL.
//int? is used because of LinqToSQL complains about converting int? to int
var skills = new List<int?>(){1,2,3}; 
//This initial query is also a small bow to LinqToSQL; Really I just wanted a plain
//List so that the Except and Any clauses can be used in the final query.
//LinqToSQL can apparently only use Contains; that may or may not be an issue with
//LinqToEntities.  Also, its not a bad idea to filter the people we need to look at
//in case there are a large number anyway.
var peopleOfInterest = PeopleSkills.Where( p => skills.Contains(p.SkillID)).ToList();   

//Final query is relatively simple, using the !x.Except(y).Any() method to 
//determine if one list is a subset of another or not.
var peopleWithAllSkills = 
    //Get a distinct list of people
    from person in peopleOfInterest.Select( p=>p.PersonID).Distinct()
    //determine what skills they have
    let personSkills = peopleOfInterest.Where(x=>x.PersonID == person).Select(x=>x.SkillID)
    //check to see if any of the skills we are looking for are not skills they have
    where !skills.Except(personSkills).Any()
    select person;
like image 61
Bert Avatar answered Sep 23 '22 07:09

Bert