How to select the records that have relation with all the values of a list?

4

I have the following sql:

SELECT DISTINCT cp_pessoa.id, cp_pessoa.nome
   FROM cp_pessoa
LEFT JOIN cp_habilidade_freelancer ON (cp_habilidade_freelancer.id_freelancer = cp_pessoa.id)
LEFT JOIN cp_habilidade ON (cp_habilidade.id = cp_habilidade_freelancer.id_habilidade)
   WHERE cp_habilidade.id = 71 OR cp_habilidade.id = 695
LIMIT 0, 10

I want only people (cp_people) who have all the skills (71, 695).

It may seem simple, but I'm having trouble.

Examples:

If I use OR the following people with the following skills (1,2,71) are returned (people without the 695 skill). If I use AND the following people with the following abilities (71, 695) are not returned

example: sqlfiddle

    
asked by anonymous 10.02.2014 / 10:02

5 answers

3

Use a subquery in the WHERE clause that counts if the person has the required qualifications.

SELECT cp_pessoa.id, cp_pessoa.nome
FROM cp_pessoa
WHERE 
      -- Se a pessoa possuir ao menos as habilitações 71 e 695
      (SELECT COUNT(DISTINCT cp_habilidade_freelancer.id_habilidade)
       FROM cp_habilidade_freelancer
       WHERE cp_habilidade_freelancer.id_freelancer = cp_pessoa.id
         AND (cp_habilidade_freelancer.id_habilidade = 71 
           OR cp_habilidade_freelancer.id_habilidade = 695)
       ) = 2
LIMIT 0, 10

Taking advantage of the @ user4919 example: link

    
10.02.2014 / 13:03
1

I can not be sure if this is correct because I can not test.

SELECT DISTINCT cp_pessoa.id, cp_pessoa.nome
   FROM cp_pessoa
LEFT JOIN cp_habilidade_freelancer ON (cp_habilidade_freelancer.id_freelancer = cp_pessoa.id)
LEFT JOIN cp_habilidade ON (cp_habilidade.id = cp_habilidade_freelancer.id_habilidade)
   WHERE (SELECT COUNT(*) FROM cp_habilidade_freelancer WHERE (id_habilidade = 71 OR id_habilidade = 695) AND cp_habilidade_freelancer.id_freelancer = cp_pessoa.id) = 2
LIMIT 0, 10
    
10.02.2014 / 13:03
0

Use IN () in this way several values can be passed by comma.

    SELECT DISTINCT cp_pessoa.id, cp_pessoa.nome
   FROM cp_pessoa
LEFT JOIN cp_habilidade_freelancer ON (cp_habilidade_freelancer.id_freelancer = cp_pessoa.id)
LEFT JOIN cp_habilidade ON (cp_habilidade.id = cp_habilidade_freelancer.id_habilidade)
   WHERE cp_habilidade.id IN(71,695)
LIMIT 0, 10
    
10.02.2014 / 10:42
0

Well, since you have a n-m (many-to-many) table, the difference is that your WHERE field will have to be filtered by a subquery or a complex JOIN. JOINs tend to be more performative than subqueries, but it's a matter of testing.

The simplest way, which can perform equal to or better than subquery / joins, especially for tables with many data, is to break your query in two. At first you access the cp_habilidade_freelancer table and get all id_freelancer that satisfy your query. Then, with these IDs, you can instead use IN() in the main query.

The way to do this in a single step is to take advantage of the query in the simple way and put it inside the main query. But be careful when your table is large and does not fit in memory.

    
10.02.2014 / 12:09
0

I believe that using inner join and group by you resolve. Follow:

SELECT cp.id, cp.nome
FROM cp_pessoa as cp
inner JOIN  cp_habilidade_freelancer  as chf ON ( chf.id_freelancer=cp.id)
left   JOIN cp_habilidade ch ON (ch.id = chf.id_habilidade) 
and  (chf.id_habilidade =2 and  chf.id_habilidade =1)
group by (nome)
    
10.02.2014 / 23:02