Как я могу уточнить этот запрос? - PullRequest
1 голос
/ 05 августа 2020

Возможно, вы захотите взглянуть на мой предыдущий вопрос .

Моя схема базы данных выглядит так

         ---------------                              ---------------   
         | candidate 1 |                              | candidate 2 |
         --------------- \                             --------------      
           /              \                                 |
       -------              --------                        etc
       |job 1|              | job 2 |  
       -------              ---------  
        /     \              /      \  
  ---------   ---------  ---------   --------  
  |company |  | skills | |company | | skills |  
  ---------   ---------  ---------- ----------  

Вот моя база данных:

mysql> describe jobs;
+--------------+---------+------+-----+---------+----------------+
| Field        | Type    | Null | Key | Default | Extra          |
+--------------+---------+------+-----+---------+----------------+
| job_id       | int(11) | NO   | PRI | NULL    | auto_increment |
| candidate_id | int(11) | NO   | MUL | NULL    |                |
| company_id   | int(11) | NO   | MUL | NULL    |                |
| start_date   | date    | NO   | MUL | NULL    |                |
| end_date     | date    | NO   | MUL | NULL    |                |
+--------------+---------+------+-----+---------+----------------+

.

mysql> describe candidates;
+----------------+----------+------+-----+---------+----------------+
| Field          | Type     | Null | Key | Default | Extra          |
+----------------+----------+------+-----+---------+----------------+
| candidate_id   | int(11)  | NO   | PRI | NULL    | auto_increment |
| candidate_name | char(50) | NO   | MUL | NULL    |                |
| home_city      | char(50) | NO   | MUL | NULL    |                |
+----------------+----------+------+-----+---------+----------------+

.

mysql> describe companies;
+-------------------+---------------+------+-----+---------+----------------+

| Field             | Type          | Null | Key | Default | Extra          |
+-------------------+---------------+------+-----+---------+----------------+
| company_id        | int(11)       | NO   | PRI | NULL    | auto_increment |
| company_name      | char(50)      | NO   | MUL | NULL    |                |
| company_city      | char(50)      | NO   | MUL | NULL    |                |
| company_post_code | char(50)      | NO   |     | NULL    |                |
| latitude          | decimal(11,8) | NO   |     | NULL    |                |
| longitude         | decimal(11,8) | NO   |     | NULL    |                |
+-------------------+---------------+------+-----+---------+----------------+

.

Обратите внимание, что мне, вероятно, следует называть это skill_usage, поскольку это указывает, когда навык был используйте don a job.

mysql> describe skills;
+----------+---------+------+-----+---------+-------+
| Field    | Type    | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| skill_id | int(11) | NO   | MUL | NULL    |       |
| job_id   | int(11) | NO   | MUL | NULL    |       |
+----------+---------+------+-----+---------+-------+

.

mysql> describe skill_names;
+------------+----------+------+-----+---------+----------------+
| Field      | Type     | Null | Key | Default | Extra          |
+------------+----------+------+-----+---------+----------------+
| skill_id   | int(11)  | NO   | PRI | NULL    | auto_increment |
| skill_name | char(32) | NO   | MUL | NULL    |                |
+------------+----------+------+-----+---------+----------------+

До сих пор мой запрос MySQL выглядел так:

SELECT DISTINCT can.candidate_id, 
                can.candidate_name, 
                     can.candidate_city,        
                     j.job_id, 
                     j.company_id,
                DATE_FORMAT(j.start_date, "%b %Y")  AS start_date, 
                DATE_FORMAT(j.end_date, "%b %Y") AS end_date,        
                s.skill_id  
FROM  candidates AS can       
  INNER JOIN jobs AS j ON j.candidate_id = can.candidate_id     
  INNER JOIN companies AS co ON j.company_id = co.company_id        
         INNER JOIN skills AS s ON s.job_id = j.job_id 
            INNER JOIN skill_names AS sn ON s.skill_id = s.skill_id 
   AND sn.skill_id = s.skill_id 
ORDER by can.candidate_id, j.job_id

Я получаю результат как это, но меня это не устраивает.

   +--------------+----------------+---------------------+--------+------------+------------+------------+----------+
   | candidate_id | candidate_name | candidate_city      | job_id | company_id | start_date | end_date   | skill_id |
   +--------------+----------------+---------------------+--------+------------+------------+------------+----------+
   |            1 | Pamela Brown   | Cardiff             |      1 |          3 | 2019-01-01 | 2019-08-31 |        1 |
   |            1 | Pamela Brown   | Cardiff             |      1 |          3 | 2019-01-01 | 2019-08-31 |        2 |
   |            1 | Pamela Brown   | Cardiff             |      1 |          3 | 2019-01-01 | 2019-08-31 |        1 |
   |            1 | Pamela Brown   | Cardiff             |      2 |          2 | 2018-06-01 | 2019-01-31 |        3 |
   |            1 | Pamela Brown   | Cardiff             |      3 |          1 | 2017-11-01 | 2018-06-30 |        4 |
   |            1 | Pamela Brown   | Cardiff             |      3 |          1 | 2017-11-01 | 2018-06-30 |        5 |
   |            1 | Pamela Brown   | Cardiff             |      3 |          1 | 2017-11-01 | 2018-06-30 |        6 |
   |            1 | Pamela Brown   | Cardiff             |      4 |          3 | 2016-08-01 | 2017-11-30 |        1 |
   |            2 | Christine Hill | Salisbury           |      5 |          2 | 2018-02-01 | 2019-05-31 |        3 |

Теперь я хотел бы ограничить поиск, указав «навык», например Python, C, C ++, UML и т. д. c и названия компаний

Пользователь вводит что-то вроде Python AND C++ в поле поиска навыков (и / или Microsoft OR Google в поле поиска по названию компании).

Как ввести это в мой запрос? Имейте в виду, что с каждым идентификатором навыка связан идентификатор задания. Может быть, мне сначала нужно преобразовать имена навыков из поиска (в данном случае Python C++) в идентификаторы навыков? Тем не менее, как мне включить это в свой запрос?

Чтобы прояснить несколько моментов:

  • и поле поиска навыков и компании может быть пустым, что я буду интерпретировать как "вернуть все"
  • поисковые запросы могут включать ключевые слова И и ИЛИ с группировочными скобками (НЕ требуется). Я достаточно счастлив, чтобы проанализировать это в PHP и превратить его в термин запроса MySQL (моя трудность только с SQL, а не PHP)

Похоже, я сделал начало, с этого INNER JOIN skills AS s ON s.job_id = j.job_id, который, я думаю, справится с поиском отдельного навыка, учитывая его ... имя? ... Id?

Полагаю, мой вопрос будет заключаться в том, как бы выглядел этот запрос, если бы, например, я хотел ограничить результаты для всех, кто работал в Microsoft OR Google и имеет навыки Python AND C++?

Если я получу пример для этого, я могу экстраполировать, но на данный момент я не уверен, хочу ли я больше INNER JOINs или предложений WHERE.

I think что я хочу расширить эту вторую последнюю строку AND sn.skill_id = s.skill_id, разделив строку поиска навыков, в моем примере Python AND C++ и сгенерировав некоторые SQL по строкам AND (s.skill_id = X ), где X - это идентификатор навыка для Python , НО Я не знаю, как обрабатывать Python AND C++ или что-то более сложное, например Python AND (C OR C++) ...

Обновить

Чтобы было ясно, пользователи технически подготовлены и ожидают, что смогут вводить сложные запросы. Например, для навыков: (C AND kernel)OR (C++ AND realtime) OR (Doors AND (UML OR QT)).

Финальное обновление

Требования только что изменились. Человек, для которого я это кодирую, только что сказал мне, что если кандидат соответствует критериям поиска по навыкам на любой работе, на которой он когда-либо работал, то я должен вернуть ВСЕ вакансии для этого кандидата.

Мне это кажется нелогичным, но он клянется, что это то, чего он хочет. Я не уверен, что это можно сделать даже в одном запросе (я рассматриваю несколько запросов; сначала нужно получить кандидатов с соответствующими навыками, затем второй - получить все их рабочие места).

Ответы [ 2 ]

3 голосов
/ 10 августа 2020

Первое, что я бы сказал, это то, что вашему исходному запросу, вероятно, требуется внешнее соединение в таблице навыков - в его нынешнем виде он извлекает только тех людей, чья работа имеет определенный навык (а это может быть не все задания). Вы говорите, что «и поле поиска навыков и компании может быть пустым, что я буду интерпретировать как вернуть все» - эта версия запроса не вернет все.

Во-вторых, я бы переименовал ваши «навыки» таблицу в "job_skills", а ваши "skill_names" в "навыки" - это более согласованно (таблица ваших компаний не называется company_names).

Отображаемый вами запрос имеет дублирование - AND sn.skill_id = s.skill_id дублирует условия ваше присоединение. Это намеренно?

Чтобы ответить на ваш вопрос: я бы представил навыки вашим пользователям в виде некоторого заранее определенного списка в вашем PHP, связанного с skill_id. Вы можете указать все навыки с помощью флажков или разрешить пользователю начать вводить текст и использовать AJAX для поиска навыков, соответствующих тексту. Это решает проблему пользовательского интерфейса (что, если пользователь пытается найти навык, которого не существует?), И немного упрощает SQL.

Тогда ваш запрос выглядит следующим образом:

SELECT DISTINCT can.candidate_id, 
                can.candidate_name, 
                can.candidate_city,        
                j.job_id, 
                j.company_id,
                DATE_FORMAT(j.start_date, "%b %Y")  AS start_date, 
                DATE_FORMAT(j.end_date, "%b %Y") AS end_date,        
                s.skill_id  
FROM  candidates AS can       
  INNER JOIN jobs AS j ON j.candidate_id = can.candidate_id     
  INNER JOIN companies AS co ON j.company_id = co.company_id        
  INNER JOIN skills AS s ON s.job_id = j.job_id 
  INNER JOIN skill_names AS sn ON s.skill_id = s.skill_id 
AND skill_id in (?, ?, ?)
OR skill_id in (?)
ORDER by can.candidate_id, j.job_id

Вам нужно заменить вопросительный знак на ввод, введенный вашими пользователями. РЕДАКТИРОВАТЬ

Проблема с разрешением пользователям вводить навыки в виде произвольного текста заключается в том, что вам тогда придется иметь дело с преобразованием регистра, пробелами и опечатками. Например, «python» - это то же самое, что «Python»? Вероятно, ваш пользователь так и задумал, но вы не можете провести простое сравнение с skill_name. Если вы хотите разрешить свободный текст, одним из решений может быть добавление «нормализованного» столбца skill_name, в котором вы сохраняете имя в согласованном формате (например, «весь верхний регистр, без пробелов»), и вы нормализуете свои входные значения в таким же образом, затем сравните с этим нормализованным столбцом. В этом случае предложение in становится примерно таким:

AND skill_id in (select skill_id from skill_name where skill_name_normalized in (?, ?, ?))

Логическое логическое значение c, которое вы упомянули - (C OR C ++) AND (Agile) - становится довольно сложным. В итоге вы пишете «визуальный конструктор запросов». Возможно, вы захотите погуглить этот термин - есть несколько хороших примеров.

Вы несколько сузили свои требования (я могу неправильно понять). Я считаю, что ваши требования:

Я хочу иметь возможность указывать ноль или более фильтров . Фильтр состоит из одной или нескольких И групп навыков . Группа навыков состоит из одного или нескольких навыков . Фильтры объединяются вместе для создания запроса.

Чтобы сделать это конкретным, давайте воспользуемся вашим примером - (A and (B OR C)) OR (D AND (E OR F)). Есть два фильтра: (A and (B OR C)) и (D AND (E OR F)). Первый фильтр имеет две группы навыков: A и (B OR C).

Трудно объяснить предложение в тексте, но вы можете создать пользовательский интерфейс, который позволяет пользователям указывать индивидуальные «фильтры». Каждый «фильтр» позволит пользователю указать одно или несколько «in clauses», соединенных «и». Затем вы можете преобразовать это в SQL - опять же, используя ваш пример, запрос SQL станет

SELECT DISTINCT can.candidate_id, 
                can.candidate_name, 
                can.candidate_city,        
                j.job_id, 
                j.company_id,
                DATE_FORMAT(j.start_date, "%b %Y")  AS start_date, 
                DATE_FORMAT(j.end_date, "%b %Y") AS end_date,        
                s.skill_id  
FROM  candidates AS can       
  INNER JOIN jobs AS j ON j.candidate_id = can.candidate_id     
  INNER JOIN companies AS co ON j.company_id = co.company_id        
  INNER JOIN skills AS s ON s.job_id = j.job_id 
  INNER JOIN skill_names AS sn ON s.skill_id = s.skill_id 
AND 
  (skill_id in (A) and skil_id in (B, C))
OR 
  (skill_id in (D) and skil_id in (E, F))
ORDER by can.candidate_id, j.job_id
1 голос
/ 11 августа 2020

Немного отличается от предыдущих комментариев и ответов ... при обработке ввода, например (A and (B OR C)) OR (D AND (E OR F)) - это блокировщик, который вы можете попробовать переместить некоторые условные logi c из объединений и вместо этого отфильтровать.

WHERE (
          ((sn.skill_id LIKE 'A') AND ((sn.skill_id LIKE ('B')) OR (sn.skill_id LIKE('C')))) 
       AND ((co.company_id IN (1,2,3)) AND ((can.city = 'Springfield') OR (j.city LIKE('Mordor'))))
     )

Вы можете построить строку запроса на основе использованного ввода, найти идентификаторы для выбранные значения и поместите их в строку и условно постройте столько фильтров, сколько захотите. Подумайте о настройке функций add_and_filter и add_or_filter для создания операторов <db>.<field> <CONDITION> <VALUE>.

$qs = "";
$qs .= "select val from table";
...
$qs .= " WHERE ";
if($userinput){ $qs += add_and_filter($userinput); }

в качестве альтернативы, посмотрите на шаблон карты / сокращения, а не пытайтесь сделать все это в SQL?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...