【问题标题】:select the candidates with all the skills using GROUP_CONCAT使用 GROUP_CONCAT 选择具有所有技能的候选人
【发布时间】:2017-07-31 07:57:30
【问题描述】:

我有 candidatescandidate-skillsskills 的 MySql 表。 选择具备所有技能的候选人

的最佳方法是什么

我尝试使用以下查询。但这并不准确。

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` 
where s.id in ('8','10') 
GROUP BY `t`.`id` 
ORDER BY `t`.`id` desc

我想要的两点是:

  1. 应显示所有技能(在评论 where 条件时)
  2. 将显示所有技能的记录。 (当我使用数组中的位置时,也会显示具有一项技能的记录)

我正在使用 codeigniter 框架。

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

【问题讨论】:

    标签: php mysql codeigniter codeigniter-3


    【解决方案1】:

    使用have子句代替where。

    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
    

    在 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();
    

    【讨论】:

      【解决方案2】:

      最灵活的方式是使用多个 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)) {
          ...
      }
      

      【讨论】:

        【解决方案3】:

        也许是这样

        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)
        

        【讨论】:

        • 这是一个很好的解决方案,但仍然存在问题。技能 ID 可能是 5、6、7、8,而我们想要的可能是 5 和 7。在这种情况下,此解决方案将不起作用。
        • 您可以拆分 where 条件,并为每个技能 ID 加入 and。
        猜你喜欢
        • 1970-01-01
        • 2010-12-28
        • 1970-01-01
        • 1970-01-01
        • 2019-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多