最灵活的方式是使用多个 JOIN; GROUP_CONCAT 和逗号分隔的列表被认为是一种反模式,如果连接没有完全按照正确的顺序完成(技能集 1、2、5 被认为与 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')
...
;
这可以轻松定制技能,例如,如果每个技能都有一个技能级别,并且您需要滑雪板的技能达到或超过 5 级。这种灵活性与GROUP_CONCAT 简直是天壤之别。
但是对于简单的匹配,你可以通过选择你想要的技能并计算它们来更快地完成它:
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;
(在更合适的 SQL 中,您需要明确指出 c 的所有字段而不是“c.*”,并在 GROUP BY 子句中重复它们。只要您按 c 分组,更聪明的 RDBMS 服务器就不会关心主键。MySQL 目前无论如何都不关心,但在严格模式下,它会关心)。
对于每个技能,您对技能运行单个快速查询以检索其 ID 并组合上面的查询。
或者,只要您的技能完全匹配,您就可以在单个更大的查询中执行:
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;
既然你想在 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)) {
...
}