Построение SQL-запроса на основе отношения один ко многим - PullRequest
1 голос
/ 17 октября 2011

У меня есть страница поиска, где я пытаюсь построить сложное условие поиска на двух таблицах, которые выглядят примерно так:

Users
ID     NAME
1      Paul
2      Remy

...

Profiles
FK_USERS_ID   TOPIC      TOPIC ID
1             language   1
1             language   2
1             expertise  1
1             expertise  2
1             expertise  3
2             language   1
2             language   2

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

Для поиска необходимо найти что-то вроде where user name LIKE %PAU%, и у пользователя "есть" язык 1, язык 2, опыт 1 и опыт 2.

Любая помощь будет принята с благодарностью!Я выполняю LEFT JOIN на двух столах, хотя я не уверен, что это правильный выбор.Моя главная проблема лежит на «И».Один и тот же пользователь должен иметь оба языка 1 и 2, и в то же время опыт 1 и 2.

Я работаю в PHP и обычно стараюсь избегать внутренних SELECT и даже объединений, но я думаю, что внутренний SELECTнеизбежно здесь?

Ответы [ 4 ]

2 голосов
/ 17 октября 2011

Вы можете сделать это, создав набор пользователей, который соответствует критериям из таблиц вашего профиля, примерно так:

SELECT FK_USERS_ID 
FROM Profiles 
WHERE topic='x' 
AND TOPIC_ID IN (1,2) 
GROUP BY FK_USERS_ID 
HAVING COUNT(1) = 2

Здесь вы перечисляете своих пользователей, которые соответствуют темам, которые вам нужны. Группируя по идентификатору пользователя и указав количество строк, которые должны быть возвращены, вы можете фактически сказать «только те, которые имеют x и y в теме z. Просто убедитесь, что COUNT (1) = x имеет одинаковое количество различных TOPIC_ID для поиска.

Затем вы можете запросить таблицу пользователя

SELECT ID 
FROM Users 
WHERE name like '%PAU%' 
AND ID IN (<insert above query here>)

Вы также можете сделать это в соединении и производной таблице, но суть должна быть объяснена выше.

EDIT: если вы ищете несколько комбинаций, вы можете использовать многостолбцевую строку mysql IN:

SELECT FK_USERS_ID 
FROM Profiles 
WHERE (topic,topic_id) IN (('x',3),('x',5),('y',3),('y',6))
GROUP BY FK_USERS_ID 
HAVING COUNT(1) = 4

Это будет искать варианты использования пар x-3, x-5, y-3 и y-6.

Вы должны иметь возможность легко создавать пары topic-topic_id в php и вставлять их в строку SQL, а также просто подсчитывать количество сгенерированных вами пар в переменную для использования для числа count (1). См. http://www.mysqlperformanceblog.com/2008/04/04/multi-column-in-clause-unexpected-mysql-issue/ для обсуждения производительности с использованием этого подхода.

1 голос
/ 17 октября 2011

Разве это не простой классический INNER JOIN?

SELECT
  p.topic, p.topic_id
FROM
  profiles p
INNER JOIN
  users u
ON
  u.id = p.fk_users_id
WHERE
  u.name LIKE '%Paul%'

Этот запрос вернул бы все языки и опыт с их идентификаторами для пользователей, соответствующих шаблону, в данном случае с именем Пола в их имени.,Это то, что тебе нравится?Или что-то еще?

0 голосов
/ 17 октября 2011

Я бы сделал несколько раз, основываясь на условиях JOIN, для каждого условия, которое вам «требуется».Я также хотел бы обеспечить индекс для таблицы профилей на основе каждой части ключа, который ищет ... (FK_User_ID, Topic_ID, Topic)

SELECT STRAIGHT_JOIN
      U.ID 
   FROM Users U
      JOIN Profiles P1
         on U.ID = P1.FK_User_ID
         AND P1.Topic_Id = 1
         AND P1.Topic = "language"
      JOIN Profiles P2
         on U.ID = P2.FK_User_ID
         AND P2.Topic_Id = 2
         AND P2.Topic = "language"
      JOIN Profiles P3
         on U.ID = P3.FK_User_ID
         AND P3.Topic_Id = 1
         AND P3.Topic = "expertise"
      JOIN Profiles P4
         on U.ID = P4.FK_User_ID
         AND P4.Topic_Id = 2
         AND P4.Topic = "expertise"
   WHERE
      u.name like '%PAU%' 

Таким образом, любые дополнительные критерии, как указано в другом ответепри условии, не должно быть слишком много воздействия.Таблицы настраиваются по критериям, как если бы они были одновременными, и если таковые отсутствуют, они будут немедленно исключены из результата, вместо того, чтобы пытаться выполнить подсчет подвыбора для каждой записи (что, я думаю, может быть задержкой, с которой вы столкнулись).

Итак, каждый из ваших "обязательных" критериев будет принимать одну и ту же конструкцию "JOIN", и, как вы можете видеть, я просто увеличиваю "псевдоним" экземпляра соединения.

0 голосов
/ 17 октября 2011
select *
from users u, profiles p
where u.id = p.fk_users_id
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'language' 
            and topic_id = 1)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'language' 
            and topic_id = 22)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'expertise' 
            and topic_id = 1)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'expertise' 
            and topic_id = 1)
and u.name like '%PAU%'

РЕДАКТИРОВАТЬ:

Хорошо, небольшое отклонение от ответа @cairnz:

SELECT ID 
FROM Users 
WHERE name like '%PAU%' 
AND ID IN (SELECT FK_USERS_ID 
           FROM Profiles 
           WHERE topic='x' 
           AND ((TOPIC_ID = 1 AND TOPIC = 'language') 
                OR (TOPIC_ID = 2 AND TOPIC = 'language')
                OR (TOPIC_ID = 1 AND TOPIC = 'expertise')
                OR (TOPIC_ID = 2 AND TOPIC = 'expertise'))
           GROUP BY FK_USERS_ID 
           HAVING COUNT(1) = 4)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...