2017-07-31 1 views
0

J'ai les tables MySQL pour candidates, candidate-skills, et skills. Quelle est la meilleure façon de sélectionner les candidats avec toutes les compétencessélectionnez les candidats avec toutes les compétences en utilisant GROUP_CONCAT

J'ai essayé d'utiliser la requête suivante. Mais ce n'est pas exact.

Les deux points sont ce que je veux:

  1. Toutes les compétences doivent être présentés (en commentant les conditions où la)
  2. enregistrements avec toutes les compétences doivent être indiquées. (Les enregistrements avec une compétence est également montrer que j'utilise l'endroit où le tableau)

J'utilise codeigniter framework.

http://sqlfiddle.com/#!9/b75c3/49

Répondre

1

au lieu d'où l'utilisation clause having.

select `t`.*, GROUP_CONCAT(DISTINCT(s.name)) as skills, 
GROUP_CONCAT(DISTINCT(s.id)) as skill_ids 
FROM `candidates` `t` 
LEFT JOIN `candidate-skills` `cs` ON `t`.`id` = `cs`.`can_id` 
LEFT JOIN `skills` `s` ON `cs`.`skill_id` = `s`.`id` 
GROUP BY `t`.`id` 
having find_in_set ('8', skill_ids) and find_in_set ('10', skill_ids) 
ORDER BY `t`.`id` desc 

dans Codeigniter

//take all skill ids in array 
$ids=['8','10']; 
$this->db->select("t.*"); 
$this->db->select("GROUP_CONCAT(DISTINCT(s.name)) as skills"); 
$this->db->select("GROUP_CONCAT(DISTINCT(s.id)) as skill_ids"); 
$this->db->from("candidates t"); 
$this->db->join("candidate-skills cs","t.id = cs.can_id"); 
$this->db->join("skills s","cs.skill_id = s.id"); 
$this->db->group_by("t.id"); 
foreach ($ids as $id) { 
$this->db->having("find_in_set ('$id', skill_ids)"); 
} 
$this->db->order_by("t.id","desc"); 
$query=$this->db->get(); 
$candidates=$query->result(); 
0

Peut-être que ce

select t.* , 
     s.skills,s.skills_id 
FROM `candidates` `t` 
join 
(
select t.id tid, group_concat(s.name) skills, group_concat(s.id order by s.id) skills_id 
FROM `candidates` `t` 
LEFT JOIN `candidate-skills` `cs` ON `t`.`id` = `cs`.`can_id` 
LEFT JOIN `skills` `s` ON `cs`.`skill_id` = `s`.`id` 
group by t.id 
) s 
on s.tid = t.id 
where instr(skills_id,'8,10') > 0 

+----+----------------+---------------------+-------------+------------+-------------------------------------------+-------------+ 
| id | name   | created_on   | modified_on | is_deleted | skills         | skills_id | 
+----+----------------+---------------------+-------------+------------+-------------------------------------------+-------------+ 
| 1 | Eugine   | 2017-05-23 11:44:30 | NULL  | N   | zend framework 2,bootstrap,wordpress  | 8,10,12  | 
| 2 | Frinoy Francis | 2017-05-23 16:44:29 | NULL  | N   | html,html5,zend framework 2,php,bootstrap | 1,4,8,10,11 | 
+----+----------------+---------------------+-------------+------------+-------------------------------------------+-------------+ 
2 rows in set (0.03 sec) 

MariaDB [sandbox]> select t.* , 
    -> s.skills,s.skills_id 
    -> FROM `candidates` `t` 
    -> join 
    -> (
    -> select t.id tid, group_concat(s.name) skills, group_concat(s.id order by s.id) skills_id 
    -> FROM `candidates` `t` 
    -> LEFT JOIN `candidate-skills` `cs` ON `t`.`id` = `cs`.`can_id` 
    -> LEFT JOIN `skills` `s` ON `cs`.`skill_id` = `s`.`id` 
    -> group by t.id 
    ->) s 
    -> on s.tid = t.id 
    -> where instr(skills_id,'') > 0 
    -> ; 
+----+----------------+---------------------+-------------+------------+-------------------------------------------+-------------+ 
| id | name   | created_on   | modified_on | is_deleted | skills         | skills_id | 
+----+----------------+---------------------+-------------+------------+-------------------------------------------+-------------+ 
| 1 | Eugine   | 2017-05-23 11:44:30 | NULL  | N   | zend framework 2,bootstrap,wordpress  | 8,10,12  | 
| 2 | Frinoy Francis | 2017-05-23 16:44:29 | NULL  | N   | html,html5,zend framework 2,php,bootstrap | 1,4,8,10,11 | 
| 3 | Arun   | 2017-05-28 12:56:24 | NULL  | N   | bootstrap         | 8   | 
+----+----------------+---------------------+-------------+------------+-------------------------------------------+-------------+ 
3 rows in set (0.03 sec) 
+0

Il est bon solution mais il y a toujours un problème. Les identifiants de compétence peuvent être 5,6,7,8 et ce que nous voulons peut être 5 et 7. Dans cette condition, cette solution ne fonctionnera pas. –

+0

Vous pouvez diviser la condition where et joindre un et pour chaque skill_id. –

1

La façon la plus souple utilise plusieurs jointures; Les listes GROUP_CONCAT et les listes séparées par des virgules sont considérées comme un anti-modèle et cela peut ne pas fonctionner si la concaténation n'est pas faite exactement dans le bon ordre (l'ensemble de compétences 1,2,5 est considéré comme différent de 1,5,2).

SELECT c.* FROM candidates AS c 
    JOIN candidateskills AS cs ON (cs.cand_id = c.id) 
    JOIN skills AS sk1 ON (cs.skill_id = sk1.id) 
    JOIN skills AS sk2 ON (cs.skill_id = sk2.id) 
    ...other sk(N)... 
    WHERE (sk1.skill = 'waterskiing') 
     AND (sk2.skill = 'snowboarding') 
     ... 
    ; 

Cela permet Tailoring facile de compétences si, par exemple, chaque compétence a un niveau de compétence et vous avez besoin pour le snowboard pour être qualifiés ou au-dessus de niveau 5. Ce genre de flexibilité est un enfer à voir avec GROUP_CONCAT.

Mais pour la correspondance simple, vous pouvez le faire plus rapidement en sélectionnant les compétences que vous voulez et il suffit de les compter:

SELECT c.* FROM candidates AS c 
    JOIN candidateskills AS cs ON (cs.cand_id = c.id) 
    WHERE cs.skill_id IN (1, 7, 24, 19, 115) 
GROUP BY c.id 
HAVING COUNT(1) = 5; 

(Dans SQL plus approprié, vous aurez besoin d'indiquer explicitement tous les champs de c au lieu de "c. *", et répétez-les dans la clause GROUP BY. Plus les serveurs RDBMS intelligents ne s'en soucieront pas tant que vous les groupez par la clé primaire de C. MySQL actuellement s'en fout de toute façon, mais en mode strict, il le ferait).

Pour chaque compétence, vous exécutez une seule requête rapide sur les compétences pour récupérer son ID et assembler la requête ci-dessus.

Ou vous pouvez faire en une seule requête plus aussi longtemps que vous avez une correspondance exacte pour la compétence:

SELECT c.* FROM candidates AS c 
    JOIN candidateskills AS cs ON (cs.cand_id = c.id) 
    JOIN skills AS s ON (cs.skill_id = s.id) 
    WHERE s.skill IN ('javascript', 'html5', 'php') 
GROUP BY c.id 
HAVING COUNT(1) = 3; 

Puisque vous voulez que cela en PHP:

$skills = array('javascript', 'html5', 'php'); 

$skno = count($skills); 
$set = implode(',', array_fill('?', $skno)); 
$params = $skills; 
$params[] = $skno; 

$query = "SELECT c.* FROM candidates AS c 
    JOIN candidateskills AS cs ON (cs.cand_id = c.id) 
    JOIN skills AS s ON (cs.skill_id = s.id) 
    WHERE s.skill IN ({$set}) 
GROUP BY c.id 
HAVING COUNT(1) = ?"; 

$stmt = $db->prepare($query); 
$stmt->execute($params); 
while ($candidate = $stmt->fetch(PDO::FETCH_ASSOC)) { 
    ... 
}